Skip to content

Commit

Permalink
Make payer and system program optional when extending lookup tables (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
jstarry authored Mar 16, 2022
1 parent 3252dc7 commit cffc32a
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ use {
},
solana_program_test::*,
solana_sdk::{
account::ReadableAccount,
account::{ReadableAccount, WritableAccount},
clock::Clock,
instruction::{Instruction, InstructionError},
pubkey::{Pubkey, PUBKEY_BYTES},
signature::{Keypair, Signer},
Expand Down Expand Up @@ -111,7 +112,7 @@ async fn test_extend_lookup_table() {
let instruction = extend_lookup_table(
lookup_table_address,
authority.pubkey(),
context.payer.pubkey(),
Some(context.payer.pubkey()),
new_addresses.clone(),
);

Expand Down Expand Up @@ -169,7 +170,7 @@ async fn test_extend_lookup_table_with_wrong_authority() {
let ix = extend_lookup_table(
lookup_table_address,
wrong_authority.pubkey(),
context.payer.pubkey(),
Some(context.payer.pubkey()),
new_addresses,
);

Expand All @@ -195,7 +196,7 @@ async fn test_extend_lookup_table_without_signing() {
let mut ix = extend_lookup_table(
lookup_table_address,
authority.pubkey(),
context.payer.pubkey(),
Some(context.payer.pubkey()),
new_addresses,
);
ix.accounts[1].is_signer = false;
Expand Down Expand Up @@ -226,7 +227,7 @@ async fn test_extend_deactivated_lookup_table() {
let ix = extend_lookup_table(
lookup_table_address,
authority.pubkey(),
context.payer.pubkey(),
Some(context.payer.pubkey()),
new_addresses,
);

Expand All @@ -252,7 +253,7 @@ async fn test_extend_immutable_lookup_table() {
let ix = extend_lookup_table(
lookup_table_address,
authority.pubkey(),
context.payer.pubkey(),
Some(context.payer.pubkey()),
new_addresses,
);

Expand All @@ -264,3 +265,83 @@ async fn test_extend_immutable_lookup_table() {
)
.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;
}
30 changes: 20 additions & 10 deletions programs/address-lookup-table/src/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,16 @@ pub enum ProgramInstruction {
/// 1. `[SIGNER]` Current authority
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
/// 0. `[WRITE]` Address lookup table account to extend
/// 1. `[SIGNER]` Current authority
/// 2. `[SIGNER, WRITE]` Account that will fund the table reallocation
/// 3. `[]` System program for CPI.
/// 2. `[SIGNER, WRITE, OPTIONAL]` Account that will fund the table reallocation
/// 3. `[OPTIONAL]` System program for CPI.
ExtendLookupTable { new_addresses: Vec<Pubkey> },

/// Deactivate an address lookup table, making it unusable and
Expand Down Expand Up @@ -120,18 +123,25 @@ pub fn freeze_lookup_table(lookup_table_address: Pubkey, authority_address: Pubk
pub fn extend_lookup_table(
lookup_table_address: Pubkey,
authority_address: Pubkey,
payer_address: Pubkey,
payer_address: Option<Pubkey>,
new_addresses: Vec<Pubkey>,
) -> 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(
id(),
&ProgramInstruction::ExtendLookupTable { new_addresses },
vec![
AccountMeta::new(lookup_table_address, false),
AccountMeta::new_readonly(authority_address, true),
AccountMeta::new(payer_address, true),
AccountMeta::new_readonly(system_program::id(), false),
],
accounts,
)
}

Expand Down
18 changes: 9 additions & 9 deletions programs/address-lookup-table/src/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,15 +225,6 @@ impl Processor {
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_data = lookup_table_account_ref.data();
let mut lookup_table = AddressLookupTable::deserialize(lookup_table_data)?;
Expand Down Expand Up @@ -315,6 +306,15 @@ impl Processor {

let table_key = *lookup_table_account.unsigned_key();
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(
system_instruction::transfer(&payer_key, &table_key, required_lamports),
&[payer_key],
Expand Down

0 comments on commit cffc32a

Please sign in to comment.