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

Commit 8ea238e

Browse files
committed
add an option to realloc account when configuring account with registry
1 parent a37fb00 commit 8ea238e

File tree

4 files changed

+116
-9
lines changed

4 files changed

+116
-9
lines changed

token/client/src/token.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1980,6 +1980,7 @@ where
19801980
&self,
19811981
account: &Pubkey,
19821982
elgamal_registry_account: &Pubkey,
1983+
payer: Option<&Pubkey>,
19831984
) -> TokenResult<T::Output> {
19841985
self.process_ixs::<[&dyn Signer; 0]>(
19851986
&[
@@ -1988,6 +1989,7 @@ where
19881989
account,
19891990
&self.pubkey,
19901991
elgamal_registry_account,
1992+
payer,
19911993
)?,
19921994
],
19931995
&[],

token/program-2022-test/tests/confidential_transfer.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2892,6 +2892,7 @@ async fn confidential_transfer_configure_token_account_with_registry() {
28922892
ProofData::InstructionData(&proof_data),
28932893
);
28942894

2895+
let payer_pubkey = ctx.payer.pubkey();
28952896
let instructions =
28962897
spl_elgamal_registry::instruction::update_registry(&alice.pubkey(), proof_location)
28972898
.unwrap();
@@ -2911,7 +2912,7 @@ async fn confidential_transfer_configure_token_account_with_registry() {
29112912
.create_auxiliary_token_account_with_extension_space(
29122913
&alice_account_keypair,
29132914
&alice.pubkey(),
2914-
vec![ExtensionType::ConfidentialTransferAccount],
2915+
vec![], // do not allocate space for confidential transfers
29152916
)
29162917
.await
29172918
.unwrap();
@@ -2920,6 +2921,7 @@ async fn confidential_transfer_configure_token_account_with_registry() {
29202921
.confidential_transfer_configure_token_account_with_registry(
29212922
&alice_account_keypair.pubkey(),
29222923
&elgamal_registry_address,
2924+
Some(&payer_pubkey), // test account allocation
29232925
)
29242926
.await
29252927
.unwrap();

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

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use {
1818
instruction::{AccountMeta, Instruction},
1919
program_error::ProgramError,
2020
pubkey::Pubkey,
21-
sysvar,
21+
system_program, sysvar,
2222
},
2323
spl_token_confidential_transfer_proof_extraction::instruction::{ProofData, ProofLocation},
2424
};
@@ -484,15 +484,22 @@ pub enum ConfidentialTransferInstruction {
484484
/// then the program skips the verification of the ElGamal pubkey
485485
/// validity proof as well as the token owner signature.
486486
///
487+
/// If the token account is not large enough to include the new
488+
/// cconfidential transfer extension, then optionally reallocate the
489+
/// account to increase the data size.
490+
///
487491
/// Accounts expected by this instruction:
488492
///
489493
/// * Single owner/delegate
490494
/// 0. `[writable]` The SPL Token account.
491495
/// 1. `[]` The corresponding SPL Token mint.
492496
/// 2. `[]` The ElGamal registry account.
497+
/// 3. `[signer, writable]` (Optional) The payer account to fund
498+
/// reallocation
499+
/// 4. `[]` (Optional) System program for reallocation funding
493500
///
494501
/// Data expected by this instruction:
495-
/// None
502+
/// `ConfigureAccountWithRegistryInstructionData`
496503
ConfigureAccountWithRegistry,
497504
}
498505

@@ -669,6 +676,17 @@ pub struct TransferWithFeeInstructionData {
669676
pub range_proof_instruction_offset: i8,
670677
}
671678

679+
/// Data expected by
680+
/// `ConfidentialTransferInstruction::ConfigureAccountWithRegistry`
681+
#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))]
682+
#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
683+
#[repr(C)]
684+
pub struct ConfigureAccountWithRegistryInstructionData {
685+
/// Reallocate token account if it is not large enough for the
686+
/// `ConfidentialTransfer` extension.
687+
pub reallocate_account: PodBool,
688+
}
689+
672690
/// Create a `InitializeMint` instruction
673691
pub fn initialize_mint(
674692
token_program_id: &Pubkey,
@@ -1730,19 +1748,29 @@ pub fn configure_account_with_registry(
17301748
token_account: &Pubkey,
17311749
mint: &Pubkey,
17321750
elgamal_registry_account: &Pubkey,
1751+
payer: Option<&Pubkey>,
17331752
) -> Result<Instruction, ProgramError> {
17341753
check_program_account(token_program_id)?;
1735-
let accounts = vec![
1754+
let mut accounts = vec![
17361755
AccountMeta::new(*token_account, false),
17371756
AccountMeta::new_readonly(*mint, false),
17381757
AccountMeta::new_readonly(*elgamal_registry_account, false),
17391758
];
1759+
let reallocate_account = if let Some(payer) = payer {
1760+
accounts.push(AccountMeta::new(*payer, true));
1761+
accounts.push(AccountMeta::new_readonly(system_program::id(), false));
1762+
true
1763+
} else {
1764+
false
1765+
};
17401766

17411767
Ok(encode_instruction(
17421768
token_program_id,
17431769
accounts,
17441770
TokenInstruction::ConfidentialTransferExtension,
17451771
ConfidentialTransferInstruction::ConfigureAccountWithRegistry,
1746-
&(),
1772+
&ConfigureAccountWithRegistryInstructionData {
1773+
reallocate_account: reallocate_account.into(),
1774+
},
17471775
))
17481776
}

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

Lines changed: 79 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,26 @@ use {
1515
EncryptedWithheldAmount,
1616
},
1717
memo_transfer::{check_previous_sibling_instruction_is_memo, memo_required},
18+
set_account_type,
1819
transfer_fee::TransferFeeConfig,
1920
transfer_hook, BaseStateWithExtensions, BaseStateWithExtensionsMut,
20-
PodStateWithExtensions, PodStateWithExtensionsMut,
21+
PodStateWithExtensions, PodStateWithExtensionsMut, StateWithExtensions,
2122
},
2223
instruction::{decode_instruction_data, decode_instruction_type},
2324
pod::{PodAccount, PodMint},
2425
processor::Processor,
26+
state::Account,
2527
},
2628
solana_program::{
2729
account_info::{next_account_info, AccountInfo},
2830
clock::Clock,
2931
entrypoint::ProgramResult,
3032
msg,
33+
program::invoke,
3134
program_error::ProgramError,
3235
pubkey::Pubkey,
33-
sysvar::Sysvar,
36+
system_instruction,
37+
sysvar::{rent::Rent, Sysvar},
3438
},
3539
spl_elgamal_registry::state::ElGamalRegistry,
3640
spl_pod::bytemuck::pod_from_bytes,
@@ -103,10 +107,25 @@ enum ElGamalPubkeySource<'a> {
103107
fn process_configure_account_with_registry(
104108
program_id: &Pubkey,
105109
accounts: &[AccountInfo],
110+
reallocate: bool,
106111
) -> ProgramResult {
107-
let elgamal_registry_account = accounts.get(2).ok_or(ProgramError::NotEnoughAccountKeys)?;
112+
let account_info_iter = &mut accounts.iter();
113+
let token_account_info = next_account_info(account_info_iter)?;
114+
let _mint_info = next_account_info(account_info_iter)?;
115+
let elgamal_registry_account = next_account_info(account_info_iter)?;
116+
108117
check_elgamal_registry_program_account(elgamal_registry_account.owner)?;
109118

119+
if reallocate {
120+
let payer_info = next_account_info(account_info_iter)?;
121+
let system_program_info = next_account_info(account_info_iter)?;
122+
reallocate_for_configure_account_with_registry(
123+
token_account_info,
124+
payer_info,
125+
system_program_info,
126+
)?;
127+
}
128+
110129
let elgamal_registry_account_data = &elgamal_registry_account.data.borrow();
111130
let elgamal_registry_account =
112131
pod_from_bytes::<ElGamalRegistry>(elgamal_registry_account_data)?;
@@ -124,6 +143,56 @@ fn process_configure_account_with_registry(
124143
)
125144
}
126145

146+
fn reallocate_for_configure_account_with_registry<'a>(
147+
token_account_info: &AccountInfo<'a>,
148+
payer_info: &AccountInfo<'a>,
149+
system_program_info: &AccountInfo<'a>,
150+
) -> ProgramResult {
151+
let mut current_extension_types = {
152+
let token_account = token_account_info.data.borrow();
153+
let account = StateWithExtensions::<Account>::unpack(&token_account)?;
154+
account.get_extension_types()?
155+
};
156+
current_extension_types.push(ExtensionType::ConfidentialTransferAccount);
157+
let needed_account_len =
158+
ExtensionType::try_calculate_account_len::<Account>(&current_extension_types)?;
159+
160+
// if account is already large enough, return early
161+
if token_account_info.data_len() >= needed_account_len {
162+
return Ok(());
163+
}
164+
165+
// reallocate
166+
msg!(
167+
"account needs realloc, +{:?} bytes",
168+
needed_account_len - token_account_info.data_len()
169+
);
170+
token_account_info.realloc(needed_account_len, false)?;
171+
172+
// if additional lamports needed to remain rent-exempt, transfer them
173+
let rent = Rent::get()?;
174+
let new_rent_exempt_reserve = rent.minimum_balance(needed_account_len);
175+
176+
let current_lamport_reserve = token_account_info.lamports();
177+
let lamports_diff = new_rent_exempt_reserve.saturating_sub(current_lamport_reserve);
178+
if lamports_diff > 0 {
179+
invoke(
180+
&system_instruction::transfer(payer_info.key, token_account_info.key, lamports_diff),
181+
&[
182+
payer_info.clone(),
183+
token_account_info.clone(),
184+
system_program_info.clone(),
185+
],
186+
)?;
187+
}
188+
189+
// set account_type, if needed
190+
let mut token_account_data = token_account_info.data.borrow_mut();
191+
set_account_type::<Account>(&mut token_account_data)?;
192+
193+
Ok(())
194+
}
195+
127196
/// Processes a [ConfigureAccount] instruction.
128197
fn process_configure_account(
129198
program_id: &Pubkey,
@@ -1272,7 +1341,13 @@ pub(crate) fn process_instruction(
12721341
}
12731342
ConfidentialTransferInstruction::ConfigureAccountWithRegistry => {
12741343
msg!("ConfidentialTransferInstruction::ConfigureAccountWithRegistry");
1275-
process_configure_account_with_registry(program_id, accounts)
1344+
let data =
1345+
decode_instruction_data::<ConfigureAccountWithRegistryInstructionData>(input)?;
1346+
process_configure_account_with_registry(
1347+
program_id,
1348+
accounts,
1349+
data.reallocate_account.into(),
1350+
)
12761351
}
12771352
}
12781353
}

0 commit comments

Comments
 (0)