Add deactivation cooldown before address lookup tables can be closed (#22011)

This commit is contained in:
Justin Starry
2021-12-20 17:33:46 -06:00
committed by GitHub
parent c0c3d7c1f2
commit f5d1115468
8 changed files with 430 additions and 72 deletions

View File

@ -21,9 +21,13 @@ async fn test_close_lookup_table() {
let mut context = setup_test_context().await;
overwrite_slot_hashes_with_slots(&mut context, &[]);
let authority_keypair = Keypair::new();
let initialized_table = new_address_lookup_table(Some(authority_keypair.pubkey()), 0);
let lookup_table_address = Pubkey::new_unique();
let authority_keypair = Keypair::new();
let initialized_table = {
let mut table = new_address_lookup_table(Some(authority_keypair.pubkey()), 0);
table.meta.deactivation_slot = 0;
table
};
add_lookup_table_account(&mut context, lookup_table_address, initialized_table).await;
let client = &mut context.banks_client;
@ -49,7 +53,7 @@ async fn test_close_lookup_table() {
}
#[tokio::test]
async fn test_close_lookup_table_too_recent() {
async fn test_close_lookup_table_not_deactivated() {
let mut context = setup_test_context().await;
let authority_keypair = Keypair::new();
@ -63,10 +67,38 @@ async fn test_close_lookup_table_too_recent() {
context.payer.pubkey(),
);
// The ix should fail because the table hasn't been deactivated yet
assert_ix_error(
&mut context,
ix,
Some(&authority_keypair),
InstructionError::InvalidArgument,
)
.await;
}
#[tokio::test]
async fn test_close_lookup_table_recently_deactivated() {
let mut context = setup_test_context().await;
let authority_keypair = Keypair::new();
let initialized_table = {
let mut table = new_address_lookup_table(Some(authority_keypair.pubkey()), 0);
table.meta.deactivation_slot = 0;
table
};
let lookup_table_address = Pubkey::new_unique();
add_lookup_table_account(&mut context, lookup_table_address, initialized_table).await;
let ix = close_lookup_table(
lookup_table_address,
authority_keypair.pubkey(),
context.payer.pubkey(),
);
// Context sets up the slot hashes sysvar to have an entry
// for slot 0 which is what the default initialized table
// has as its derivation slot. Because that slot is present,
// the ix should fail.
// for slot 0 which is when the table was deactivated.
// Because that slot is present, the ix should fail.
assert_ix_error(
&mut context,
ix,

View File

@ -52,7 +52,7 @@ async fn test_create_lookup_table() {
Rent::default().minimum_balance(LOOKUP_TABLE_META_SIZE)
);
let lookup_table = AddressLookupTable::deserialize(&lookup_table_account.data).unwrap();
assert_eq!(lookup_table.meta.derivation_slot, test_recent_slot);
assert_eq!(lookup_table.meta.deactivation_slot, Slot::MAX);
assert_eq!(lookup_table.meta.authority, Some(authority_address));
assert_eq!(lookup_table.meta.last_extended_slot, 0);
assert_eq!(lookup_table.meta.last_extended_slot_start_index, 0);

View File

@ -0,0 +1,145 @@
use {
assert_matches::assert_matches,
common::{
add_lookup_table_account, assert_ix_error, new_address_lookup_table, setup_test_context,
},
solana_address_lookup_table_program::{
instruction::deactivate_lookup_table, state::AddressLookupTable,
},
solana_program_test::*,
solana_sdk::{
instruction::InstructionError,
pubkey::Pubkey,
signature::{Keypair, Signer},
transaction::Transaction,
},
};
mod common;
#[tokio::test]
async fn test_deactivate_lookup_table() {
let mut context = setup_test_context().await;
let authority = Keypair::new();
let mut initialized_table = new_address_lookup_table(Some(authority.pubkey()), 10);
let lookup_table_address = Pubkey::new_unique();
add_lookup_table_account(
&mut context,
lookup_table_address,
initialized_table.clone(),
)
.await;
let client = &mut context.banks_client;
let payer = &context.payer;
let recent_blockhash = context.last_blockhash;
let transaction = Transaction::new_signed_with_payer(
&[deactivate_lookup_table(
lookup_table_address,
authority.pubkey(),
)],
Some(&payer.pubkey()),
&[payer, &authority],
recent_blockhash,
);
assert_matches!(client.process_transaction(transaction).await, Ok(()));
let table_account = client
.get_account(lookup_table_address)
.await
.unwrap()
.unwrap();
let lookup_table = AddressLookupTable::deserialize(&table_account.data).unwrap();
assert_eq!(lookup_table.meta.deactivation_slot, 1);
// Check that only the deactivation slot changed
initialized_table.meta.deactivation_slot = 1;
assert_eq!(initialized_table, lookup_table);
}
#[tokio::test]
async fn test_deactivate_immutable_lookup_table() {
let mut context = setup_test_context().await;
let initialized_table = new_address_lookup_table(None, 10);
let lookup_table_address = Pubkey::new_unique();
add_lookup_table_account(&mut context, lookup_table_address, initialized_table).await;
let authority = Keypair::new();
let ix = deactivate_lookup_table(lookup_table_address, authority.pubkey());
assert_ix_error(
&mut context,
ix,
Some(&authority),
InstructionError::Immutable,
)
.await;
}
#[tokio::test]
async fn test_deactivate_already_deactivated() {
let mut context = setup_test_context().await;
let authority = Keypair::new();
let initialized_table = {
let mut table = new_address_lookup_table(Some(authority.pubkey()), 0);
table.meta.deactivation_slot = 0;
table
};
let lookup_table_address = Pubkey::new_unique();
add_lookup_table_account(&mut context, lookup_table_address, initialized_table).await;
let ix = deactivate_lookup_table(lookup_table_address, authority.pubkey());
assert_ix_error(
&mut context,
ix,
Some(&authority),
InstructionError::InvalidArgument,
)
.await;
}
#[tokio::test]
async fn test_deactivate_lookup_table_with_wrong_authority() {
let mut context = setup_test_context().await;
let authority = Keypair::new();
let wrong_authority = Keypair::new();
let initialized_table = new_address_lookup_table(Some(authority.pubkey()), 10);
let lookup_table_address = Pubkey::new_unique();
add_lookup_table_account(&mut context, lookup_table_address, initialized_table).await;
let ix = deactivate_lookup_table(lookup_table_address, wrong_authority.pubkey());
assert_ix_error(
&mut context,
ix,
Some(&wrong_authority),
InstructionError::IncorrectAuthority,
)
.await;
}
#[tokio::test]
async fn test_deactivate_lookup_table_without_signing() {
let mut context = setup_test_context().await;
let authority = Keypair::new();
let initialized_table = new_address_lookup_table(Some(authority.pubkey()), 10);
let lookup_table_address = Pubkey::new_unique();
add_lookup_table_account(&mut context, lookup_table_address, initialized_table).await;
let mut ix = deactivate_lookup_table(lookup_table_address, authority.pubkey());
ix.accounts[1].is_signer = false;
assert_ix_error(
&mut context,
ix,
None,
InstructionError::MissingRequiredSignature,
)
.await;
}

View File

@ -1,6 +1,8 @@
use {
assert_matches::assert_matches,
common::{add_lookup_table_account, new_address_lookup_table, setup_test_context},
common::{
add_lookup_table_account, assert_ix_error, new_address_lookup_table, setup_test_context,
},
solana_address_lookup_table_program::{
instruction::extend_lookup_table,
state::{AddressLookupTable, LookupTableMeta},
@ -130,7 +132,7 @@ async fn test_extend_lookup_table() {
} else {
num_existing_addresses as u8
},
derivation_slot: lookup_table.meta.derivation_slot,
deactivation_slot: lookup_table.meta.deactivation_slot,
authority: lookup_table.meta.authority,
_padding: 0u16,
},
@ -156,59 +158,111 @@ async fn test_extend_lookup_table() {
}
#[tokio::test]
async fn test_extend_addresses_authority_errors() {
async fn test_extend_lookup_table_with_wrong_authority() {
let mut context = setup_test_context().await;
let authority = Keypair::new();
let wrong_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;
for (existing_authority, ix_authority, use_signer, expected_err) in [
(
Some(authority.pubkey()),
Keypair::new(),
true,
InstructionError::IncorrectAuthority,
),
(
Some(authority.pubkey()),
authority,
false,
InstructionError::MissingRequiredSignature,
),
(None, Keypair::new(), true, InstructionError::Immutable),
] {
let lookup_table = new_address_lookup_table(existing_authority, 0);
let lookup_table_address = Pubkey::new_unique();
let _ = add_lookup_table_account(&mut context, lookup_table_address, lookup_table.clone())
.await;
let new_addresses = vec![Pubkey::new_unique()];
let ix = extend_lookup_table(
lookup_table_address,
wrong_authority.pubkey(),
context.payer.pubkey(),
new_addresses,
);
let num_new_addresses = 1;
let mut new_addresses = Vec::with_capacity(num_new_addresses);
new_addresses.resize_with(num_new_addresses, Pubkey::new_unique);
let mut instruction = extend_lookup_table(
lookup_table_address,
ix_authority.pubkey(),
context.payer.pubkey(),
new_addresses.clone(),
);
if !use_signer {
instruction.accounts[1].is_signer = false;
}
let mut expected_addresses: Vec<Pubkey> = lookup_table.addresses.to_vec();
expected_addresses.extend(new_addresses);
let extra_signer = if use_signer {
Some(&ix_authority)
} else {
None
};
let test_case = TestCase {
lookup_table_address,
instruction,
extra_signer,
expected_result: Err(expected_err),
};
run_test_case(&mut context, test_case).await;
}
assert_ix_error(
&mut context,
ix,
Some(&wrong_authority),
InstructionError::IncorrectAuthority,
)
.await;
}
#[tokio::test]
async fn test_extend_lookup_table_without_signing() {
let mut context = setup_test_context().await;
let authority = Keypair::new();
let initialized_table = new_address_lookup_table(Some(authority.pubkey()), 10);
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 mut ix = extend_lookup_table(
lookup_table_address,
authority.pubkey(),
context.payer.pubkey(),
new_addresses,
);
ix.accounts[1].is_signer = false;
assert_ix_error(
&mut context,
ix,
None,
InstructionError::MissingRequiredSignature,
)
.await;
}
#[tokio::test]
async fn test_extend_deactivated_lookup_table() {
let mut context = setup_test_context().await;
let authority = Keypair::new();
let initialized_table = {
let mut table = new_address_lookup_table(Some(authority.pubkey()), 0);
table.meta.deactivation_slot = 0;
table
};
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(),
context.payer.pubkey(),
new_addresses,
);
assert_ix_error(
&mut context,
ix,
Some(&authority),
InstructionError::InvalidArgument,
)
.await;
}
#[tokio::test]
async fn test_extend_immutable_lookup_table() {
let mut context = setup_test_context().await;
let authority = Keypair::new();
let initialized_table = new_address_lookup_table(None, 1);
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(),
context.payer.pubkey(),
new_addresses,
);
assert_ix_error(
&mut context,
ix,
Some(&authority),
InstructionError::Immutable,
)
.await;
}

View File

@ -78,6 +78,30 @@ async fn test_freeze_immutable_lookup_table() {
.await;
}
#[tokio::test]
async fn test_freeze_deactivated_lookup_table() {
let mut context = setup_test_context().await;
let authority = Keypair::new();
let initialized_table = {
let mut table = new_address_lookup_table(Some(authority.pubkey()), 10);
table.meta.deactivation_slot = 0;
table
};
let lookup_table_address = Pubkey::new_unique();
add_lookup_table_account(&mut context, lookup_table_address, initialized_table).await;
let ix = freeze_lookup_table(lookup_table_address, authority.pubkey());
assert_ix_error(
&mut context,
ix,
Some(&authority),
InstructionError::InvalidArgument,
)
.await;
}
#[tokio::test]
async fn test_freeze_lookup_table_with_wrong_authority() {
let mut context = setup_test_context().await;