Add deactivation cooldown before address lookup tables can be closed (#22011)
This commit is contained in:
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
Reference in New Issue
Block a user