Skip to content
This repository was archived by the owner on Mar 11, 2025. It is now read-only.

Commit abc77af

Browse files
Require pubkey validity proof when initializing confidential account (#3784)
1 parent 85160e0 commit abc77af

File tree

3 files changed

+79
-17
lines changed

3 files changed

+79
-17
lines changed

token/client/src/token.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1501,9 +1501,8 @@ where
15011501
authority: &S,
15021502
maximum_pending_balance_credit_counter: u64,
15031503
) -> TokenResult<T::Output> {
1504-
let elgamal_pubkey = ElGamalKeypair::new(authority, token_account)
1505-
.map_err(TokenError::Key)?
1506-
.public;
1504+
let elgamal_keypair =
1505+
ElGamalKeypair::new(authority, token_account).map_err(TokenError::Key)?;
15071506
let decryptable_zero_balance = AeKey::new(authority, token_account)
15081507
.map_err(TokenError::Key)?
15091508
.encrypt(0);
@@ -1512,7 +1511,7 @@ where
15121511
token_account,
15131512
authority,
15141513
maximum_pending_balance_credit_counter,
1515-
elgamal_pubkey,
1514+
&elgamal_keypair,
15161515
decryptable_zero_balance,
15171516
)
15181517
.await
@@ -1525,20 +1524,25 @@ where
15251524
token_account: &Pubkey,
15261525
authority: &S,
15271526
maximum_pending_balance_credit_counter: u64,
1528-
elgamal_pubkey: ElGamalPubkey,
1527+
elgamal_keypair: &ElGamalKeypair,
15291528
decryptable_zero_balance: AeCiphertext,
15301529
) -> TokenResult<T::Output> {
1530+
let proof_data =
1531+
confidential_transfer::instruction::PubkeyValidityData::new(elgamal_keypair)
1532+
.map_err(TokenError::Proof)?;
1533+
15311534
self.process_ixs(
1532-
&[confidential_transfer::instruction::configure_account(
1535+
&confidential_transfer::instruction::configure_account(
15331536
&self.program_id,
15341537
token_account,
15351538
&self.pubkey,
1536-
elgamal_pubkey.into(),
1539+
elgamal_keypair.public.into(),
15371540
decryptable_zero_balance,
15381541
maximum_pending_balance_credit_counter,
15391542
&authority.pubkey(),
15401543
&[],
1541-
)?],
1544+
&proof_data,
1545+
)?,
15421546
&[authority],
15431547
)
15441548
.await

token/program-2022/src/extension/confidential_transfer/instruction.rs

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,18 +65,23 @@ pub enum ConfidentialTransferInstruction {
6565
/// Upon success confidential deposits and transfers are enabled, use the
6666
/// `DisableBalanceCredits` instruction to disable.
6767
///
68+
/// In order for this instruction to be successfully processed, it must be accompanied by the
69+
/// `VerifyPubkey` instruction of the `zk_token_proof` program in the same transaction.
70+
///
6871
/// Accounts expected by this instruction:
6972
///
7073
/// * Single owner/delegate
7174
/// 0. `[writeable]` The SPL Token account.
7275
/// 1. `[]` The corresponding SPL Token mint.
73-
/// 2. `[signer]` The single source account owner.
76+
/// 2. `[]` Instructions sysvar.
77+
/// 3. `[signer]` The single source account owner.
7478
///
7579
/// * Multisignature owner/delegate
7680
/// 0. `[writeable]` The SPL Token account.
7781
/// 1. `[]` The corresponding SPL Token mint.
7882
/// 2. `[]` The multisig source account owner.
79-
/// 3.. `[signer]` Required M signer accounts for the SPL Token Multisig account.
83+
/// 3. `[]` Instructions sysvar.
84+
/// 4.. `[signer]` Required M signer accounts for the SPL Token Multisig account.
8085
///
8186
/// Data expected by this instruction:
8287
/// `ConfigureAccountInstructionData`
@@ -378,6 +383,9 @@ pub struct ConfigureAccountInstructionData {
378383
/// The maximum number of despots and transfers that an account can receiver before the
379384
/// `ApplyPendingBalance` is executed
380385
pub maximum_pending_balance_credit_counter: PodU64,
386+
/// Relative location of the `ProofInstruction::VerifyPubkey` instruction to the
387+
/// `ConfigureAccount` instruction in the transaction
388+
pub proof_instruction_offset: i8,
381389
}
382390

383391
/// Data expected by `ConfidentialTransferInstruction::EmptyAccount`
@@ -499,9 +507,11 @@ pub fn update_mint(
499507
}
500508

501509
/// Create a `ConfigureAccount` instruction
510+
///
511+
/// This instruction is suitable for use with a cross-program `invoke`
502512
#[allow(clippy::too_many_arguments)]
503513
#[cfg(not(target_os = "solana"))]
504-
pub fn configure_account(
514+
pub fn inner_configure_account(
505515
token_program_id: &Pubkey,
506516
token_account: &Pubkey,
507517
mint: &Pubkey,
@@ -510,11 +520,13 @@ pub fn configure_account(
510520
maximum_pending_balance_credit_counter: u64,
511521
authority: &Pubkey,
512522
multisig_signers: &[&Pubkey],
523+
proof_instruction_offset: i8,
513524
) -> Result<Instruction, ProgramError> {
514525
check_program_account(token_program_id)?;
515526
let mut accounts = vec![
516527
AccountMeta::new(*token_account, false),
517528
AccountMeta::new_readonly(*mint, false),
529+
AccountMeta::new_readonly(sysvar::instructions::id(), false),
518530
AccountMeta::new_readonly(*authority, multisig_signers.is_empty()),
519531
];
520532

@@ -531,10 +543,41 @@ pub fn configure_account(
531543
encryption_pubkey,
532544
decryptable_zero_balance: decryptable_zero_balance.into(),
533545
maximum_pending_balance_credit_counter: maximum_pending_balance_credit_counter.into(),
546+
proof_instruction_offset,
534547
},
535548
))
536549
}
537550

551+
/// Create a `ConfigureAccount` instruction
552+
#[allow(clippy::too_many_arguments)]
553+
#[cfg(not(target_os = "solana"))]
554+
pub fn configure_account(
555+
token_program_id: &Pubkey,
556+
token_account: &Pubkey,
557+
mint: &Pubkey,
558+
encryption_pubkey: EncryptionPubkey,
559+
decryptable_zero_balance: AeCiphertext,
560+
maximum_pending_balance_credit_counter: u64,
561+
authority: &Pubkey,
562+
multisig_signers: &[&Pubkey],
563+
proof_data: &PubkeyValidityData,
564+
) -> Result<Vec<Instruction>, ProgramError> {
565+
Ok(vec![
566+
inner_configure_account(
567+
token_program_id,
568+
token_account,
569+
mint,
570+
encryption_pubkey,
571+
decryptable_zero_balance,
572+
maximum_pending_balance_credit_counter,
573+
authority,
574+
multisig_signers,
575+
1,
576+
)?,
577+
verify_pubkey_validity(proof_data),
578+
])
579+
}
580+
538581
/// Create an `ApproveAccount` instruction
539582
pub fn approve_account(
540583
token_program_id: &Pubkey,

token/program-2022/src/extension/confidential_transfer/processor.rs

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -124,15 +124,15 @@ fn process_update_mint(
124124
fn process_configure_account(
125125
program_id: &Pubkey,
126126
accounts: &[AccountInfo],
127-
ConfigureAccountInstructionData {
128-
encryption_pubkey,
129-
decryptable_zero_balance,
130-
maximum_pending_balance_credit_counter,
131-
}: &ConfigureAccountInstructionData,
127+
encryption_pubkey: &EncryptionPubkey,
128+
decryptable_zero_balance: &DecryptableBalance,
129+
maximum_pending_balance_credit_counter: &PodU64,
130+
proof_instruction_offset: i64,
132131
) -> ProgramResult {
133132
let account_info_iter = &mut accounts.iter();
134133
let token_account_info = next_account_info(account_info_iter)?;
135134
let mint_info = next_account_info(account_info_iter)?;
135+
let instructions_sysvar_info = next_account_info(account_info_iter)?;
136136
let authority_info = next_account_info(account_info_iter)?;
137137
let authority_info_data_len = authority_info.data_len();
138138

@@ -157,6 +157,17 @@ fn process_configure_account(
157157
let mint = StateWithExtensions::<Mint>::unpack(mint_data)?;
158158
let confidential_transfer_mint = mint.get_extension::<ConfidentialTransferMint>()?;
159159

160+
let previous_instruction =
161+
get_instruction_relative(proof_instruction_offset, instructions_sysvar_info)?;
162+
let proof_data = decode_proof_instruction::<PubkeyValidityData>(
163+
ProofInstruction::VerifyPubkeyValidity,
164+
&previous_instruction,
165+
)?;
166+
167+
if proof_data.pubkey != *encryption_pubkey {
168+
return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into());
169+
}
170+
160171
// Note: The caller is expected to use the `Reallocate` instruction to ensure there is
161172
// sufficient room in their token account for the new `ConfidentialTransferAccount` extension
162173
let mut confidential_transfer_account =
@@ -1186,10 +1197,14 @@ pub(crate) fn process_instruction(
11861197
}
11871198
ConfidentialTransferInstruction::ConfigureAccount => {
11881199
msg!("ConfidentialTransferInstruction::ConfigureAccount");
1200+
let data = decode_instruction_data::<ConfigureAccountInstructionData>(input)?;
11891201
process_configure_account(
11901202
program_id,
11911203
accounts,
1192-
decode_instruction_data::<ConfigureAccountInstructionData>(input)?,
1204+
&data.encryption_pubkey,
1205+
&data.decryptable_zero_balance,
1206+
&data.maximum_pending_balance_credit_counter,
1207+
data.proof_instruction_offset as i64,
11931208
)
11941209
}
11951210
ConfidentialTransferInstruction::ApproveAccount => {

0 commit comments

Comments
 (0)