Make payer and system program optional when extending lookup tables (#23678)
This commit is contained in:
parent
3252dc7203
commit
cffc32af3e
|
@ -9,7 +9,8 @@ use {
|
||||||
},
|
},
|
||||||
solana_program_test::*,
|
solana_program_test::*,
|
||||||
solana_sdk::{
|
solana_sdk::{
|
||||||
account::ReadableAccount,
|
account::{ReadableAccount, WritableAccount},
|
||||||
|
clock::Clock,
|
||||||
instruction::{Instruction, InstructionError},
|
instruction::{Instruction, InstructionError},
|
||||||
pubkey::{Pubkey, PUBKEY_BYTES},
|
pubkey::{Pubkey, PUBKEY_BYTES},
|
||||||
signature::{Keypair, Signer},
|
signature::{Keypair, Signer},
|
||||||
|
@ -111,7 +112,7 @@ async fn test_extend_lookup_table() {
|
||||||
let instruction = extend_lookup_table(
|
let instruction = extend_lookup_table(
|
||||||
lookup_table_address,
|
lookup_table_address,
|
||||||
authority.pubkey(),
|
authority.pubkey(),
|
||||||
context.payer.pubkey(),
|
Some(context.payer.pubkey()),
|
||||||
new_addresses.clone(),
|
new_addresses.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -169,7 +170,7 @@ async fn test_extend_lookup_table_with_wrong_authority() {
|
||||||
let ix = extend_lookup_table(
|
let ix = extend_lookup_table(
|
||||||
lookup_table_address,
|
lookup_table_address,
|
||||||
wrong_authority.pubkey(),
|
wrong_authority.pubkey(),
|
||||||
context.payer.pubkey(),
|
Some(context.payer.pubkey()),
|
||||||
new_addresses,
|
new_addresses,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -195,7 +196,7 @@ async fn test_extend_lookup_table_without_signing() {
|
||||||
let mut ix = extend_lookup_table(
|
let mut ix = extend_lookup_table(
|
||||||
lookup_table_address,
|
lookup_table_address,
|
||||||
authority.pubkey(),
|
authority.pubkey(),
|
||||||
context.payer.pubkey(),
|
Some(context.payer.pubkey()),
|
||||||
new_addresses,
|
new_addresses,
|
||||||
);
|
);
|
||||||
ix.accounts[1].is_signer = false;
|
ix.accounts[1].is_signer = false;
|
||||||
|
@ -226,7 +227,7 @@ async fn test_extend_deactivated_lookup_table() {
|
||||||
let ix = extend_lookup_table(
|
let ix = extend_lookup_table(
|
||||||
lookup_table_address,
|
lookup_table_address,
|
||||||
authority.pubkey(),
|
authority.pubkey(),
|
||||||
context.payer.pubkey(),
|
Some(context.payer.pubkey()),
|
||||||
new_addresses,
|
new_addresses,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -252,7 +253,7 @@ async fn test_extend_immutable_lookup_table() {
|
||||||
let ix = extend_lookup_table(
|
let ix = extend_lookup_table(
|
||||||
lookup_table_address,
|
lookup_table_address,
|
||||||
authority.pubkey(),
|
authority.pubkey(),
|
||||||
context.payer.pubkey(),
|
Some(context.payer.pubkey()),
|
||||||
new_addresses,
|
new_addresses,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -264,3 +265,83 @@ async fn test_extend_immutable_lookup_table() {
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_extend_lookup_table_without_payer() {
|
||||||
|
let mut context = setup_test_context().await;
|
||||||
|
|
||||||
|
let authority = Keypair::new();
|
||||||
|
let initialized_table = new_address_lookup_table(Some(authority.pubkey()), 0);
|
||||||
|
let lookup_table_address = Pubkey::new_unique();
|
||||||
|
add_lookup_table_account(&mut context, lookup_table_address, initialized_table).await;
|
||||||
|
|
||||||
|
let new_addresses = vec![Pubkey::new_unique()];
|
||||||
|
let ix = extend_lookup_table(
|
||||||
|
lookup_table_address,
|
||||||
|
authority.pubkey(),
|
||||||
|
None,
|
||||||
|
new_addresses,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_ix_error(
|
||||||
|
&mut context,
|
||||||
|
ix,
|
||||||
|
Some(&authority),
|
||||||
|
InstructionError::NotEnoughAccountKeys,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_extend_prepaid_lookup_table_without_payer() {
|
||||||
|
let mut context = setup_test_context().await;
|
||||||
|
|
||||||
|
let authority = Keypair::new();
|
||||||
|
let lookup_table_address = Pubkey::new_unique();
|
||||||
|
|
||||||
|
let expected_state = {
|
||||||
|
// initialize lookup table
|
||||||
|
let empty_lookup_table = new_address_lookup_table(Some(authority.pubkey()), 0);
|
||||||
|
let mut lookup_table_account =
|
||||||
|
add_lookup_table_account(&mut context, lookup_table_address, empty_lookup_table).await;
|
||||||
|
|
||||||
|
// calculate required rent exempt balance for adding one address
|
||||||
|
let mut temp_lookup_table = new_address_lookup_table(Some(authority.pubkey()), 1);
|
||||||
|
let data = temp_lookup_table.clone().serialize_for_tests().unwrap();
|
||||||
|
let rent = context.banks_client.get_rent().await.unwrap();
|
||||||
|
let rent_exempt_balance = rent.minimum_balance(data.len());
|
||||||
|
|
||||||
|
// prepay for one address
|
||||||
|
lookup_table_account.set_lamports(rent_exempt_balance);
|
||||||
|
context.set_account(&lookup_table_address, &lookup_table_account);
|
||||||
|
|
||||||
|
// test will extend table in the current bank's slot
|
||||||
|
let clock = context.banks_client.get_sysvar::<Clock>().await.unwrap();
|
||||||
|
temp_lookup_table.meta.last_extended_slot = clock.slot;
|
||||||
|
|
||||||
|
ExpectedTableAccount {
|
||||||
|
lamports: rent_exempt_balance,
|
||||||
|
data_len: data.len(),
|
||||||
|
state: temp_lookup_table,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let new_addresses = expected_state.state.addresses.to_vec();
|
||||||
|
let instruction = extend_lookup_table(
|
||||||
|
lookup_table_address,
|
||||||
|
authority.pubkey(),
|
||||||
|
None,
|
||||||
|
new_addresses,
|
||||||
|
);
|
||||||
|
|
||||||
|
run_test_case(
|
||||||
|
&mut context,
|
||||||
|
TestCase {
|
||||||
|
lookup_table_address,
|
||||||
|
instruction,
|
||||||
|
extra_signer: Some(&authority),
|
||||||
|
expected_result: Ok(expected_state),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
|
@ -38,13 +38,16 @@ pub enum ProgramInstruction {
|
||||||
/// 1. `[SIGNER]` Current authority
|
/// 1. `[SIGNER]` Current authority
|
||||||
FreezeLookupTable,
|
FreezeLookupTable,
|
||||||
|
|
||||||
/// Extend an address lookup table with new addresses
|
/// Extend an address lookup table with new addresses. Funding account and
|
||||||
|
/// system program account references are only required if the lookup table
|
||||||
|
/// account requires additional lamports to cover the rent-exempt balance
|
||||||
|
/// after being extended.
|
||||||
///
|
///
|
||||||
/// # Account references
|
/// # Account references
|
||||||
/// 0. `[WRITE]` Address lookup table account to extend
|
/// 0. `[WRITE]` Address lookup table account to extend
|
||||||
/// 1. `[SIGNER]` Current authority
|
/// 1. `[SIGNER]` Current authority
|
||||||
/// 2. `[SIGNER, WRITE]` Account that will fund the table reallocation
|
/// 2. `[SIGNER, WRITE, OPTIONAL]` Account that will fund the table reallocation
|
||||||
/// 3. `[]` System program for CPI.
|
/// 3. `[OPTIONAL]` System program for CPI.
|
||||||
ExtendLookupTable { new_addresses: Vec<Pubkey> },
|
ExtendLookupTable { new_addresses: Vec<Pubkey> },
|
||||||
|
|
||||||
/// Deactivate an address lookup table, making it unusable and
|
/// Deactivate an address lookup table, making it unusable and
|
||||||
|
@ -120,18 +123,25 @@ pub fn freeze_lookup_table(lookup_table_address: Pubkey, authority_address: Pubk
|
||||||
pub fn extend_lookup_table(
|
pub fn extend_lookup_table(
|
||||||
lookup_table_address: Pubkey,
|
lookup_table_address: Pubkey,
|
||||||
authority_address: Pubkey,
|
authority_address: Pubkey,
|
||||||
payer_address: Pubkey,
|
payer_address: Option<Pubkey>,
|
||||||
new_addresses: Vec<Pubkey>,
|
new_addresses: Vec<Pubkey>,
|
||||||
) -> Instruction {
|
) -> Instruction {
|
||||||
|
let mut accounts = vec![
|
||||||
|
AccountMeta::new(lookup_table_address, false),
|
||||||
|
AccountMeta::new_readonly(authority_address, true),
|
||||||
|
];
|
||||||
|
|
||||||
|
if let Some(payer_address) = payer_address {
|
||||||
|
accounts.extend([
|
||||||
|
AccountMeta::new(payer_address, true),
|
||||||
|
AccountMeta::new_readonly(system_program::id(), false),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
Instruction::new_with_bincode(
|
Instruction::new_with_bincode(
|
||||||
id(),
|
id(),
|
||||||
&ProgramInstruction::ExtendLookupTable { new_addresses },
|
&ProgramInstruction::ExtendLookupTable { new_addresses },
|
||||||
vec![
|
accounts,
|
||||||
AccountMeta::new(lookup_table_address, false),
|
|
||||||
AccountMeta::new_readonly(authority_address, true),
|
|
||||||
AccountMeta::new(payer_address, true),
|
|
||||||
AccountMeta::new_readonly(system_program::id(), false),
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -225,15 +225,6 @@ impl Processor {
|
||||||
return Err(InstructionError::MissingRequiredSignature);
|
return Err(InstructionError::MissingRequiredSignature);
|
||||||
}
|
}
|
||||||
|
|
||||||
let payer_account =
|
|
||||||
keyed_account_at_index(keyed_accounts, checked_add(first_instruction_account, 2)?)?;
|
|
||||||
let payer_key = if let Some(payer_key) = payer_account.signer_key() {
|
|
||||||
*payer_key
|
|
||||||
} else {
|
|
||||||
ic_msg!(invoke_context, "Payer account must be a signer");
|
|
||||||
return Err(InstructionError::MissingRequiredSignature);
|
|
||||||
};
|
|
||||||
|
|
||||||
let lookup_table_account_ref = lookup_table_account.try_account_ref()?;
|
let lookup_table_account_ref = lookup_table_account.try_account_ref()?;
|
||||||
let lookup_table_data = lookup_table_account_ref.data();
|
let lookup_table_data = lookup_table_account_ref.data();
|
||||||
let mut lookup_table = AddressLookupTable::deserialize(lookup_table_data)?;
|
let mut lookup_table = AddressLookupTable::deserialize(lookup_table_data)?;
|
||||||
|
@ -315,6 +306,15 @@ impl Processor {
|
||||||
|
|
||||||
let table_key = *lookup_table_account.unsigned_key();
|
let table_key = *lookup_table_account.unsigned_key();
|
||||||
if required_lamports > 0 {
|
if required_lamports > 0 {
|
||||||
|
let payer_account =
|
||||||
|
keyed_account_at_index(keyed_accounts, checked_add(first_instruction_account, 2)?)?;
|
||||||
|
let payer_key = if let Some(payer_key) = payer_account.signer_key() {
|
||||||
|
*payer_key
|
||||||
|
} else {
|
||||||
|
ic_msg!(invoke_context, "Payer account must be a signer");
|
||||||
|
return Err(InstructionError::MissingRequiredSignature);
|
||||||
|
};
|
||||||
|
|
||||||
invoke_context.native_invoke(
|
invoke_context.native_invoke(
|
||||||
system_instruction::transfer(&payer_key, &table_key, required_lamports),
|
system_instruction::transfer(&payer_key, &table_key, required_lamports),
|
||||||
&[payer_key],
|
&[payer_key],
|
||||||
|
|
Loading…
Reference in New Issue