-
Notifications
You must be signed in to change notification settings - Fork 4.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add address lookup table program (#21616)
* Add address lookup table program * feedback
- Loading branch information
Showing
21 changed files
with
1,665 additions
and
4 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# This package only exists to avoid circular dependencies during cargo publish: | ||
# solana-runtime -> solana-address-program-runtime -> solana-program-test -> solana-runtime | ||
|
||
[package] | ||
name = "solana-address-lookup-table-program-tests" | ||
version = "1.10.0" | ||
authors = ["Solana Maintainers <[email protected]>"] | ||
repository = "https://github.com/solana-labs/solana" | ||
license = "Apache-2.0" | ||
homepage = "https://solana.com/" | ||
edition = "2021" | ||
publish = false | ||
|
||
[dev-dependencies] | ||
assert_matches = "1.5.0" | ||
bincode = "1.3.3" | ||
solana-address-lookup-table-program = { path = "../address-lookup-table", version = "=1.10.0" } | ||
solana-program-test = { path = "../../program-test", version = "=1.10.0" } | ||
solana-sdk = { path = "../../sdk", version = "=1.10.0" } | ||
|
||
[package.metadata.docs.rs] | ||
targets = ["x86_64-unknown-linux-gnu"] |
151 changes: 151 additions & 0 deletions
151
programs/address-lookup-table-tests/tests/close_lookup_table_ix.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
use { | ||
assert_matches::assert_matches, | ||
common::{ | ||
add_lookup_table_account, assert_ix_error, new_address_lookup_table, | ||
overwrite_slot_hashes_with_slots, setup_test_context, | ||
}, | ||
solana_address_lookup_table_program::instruction::close_lookup_table, | ||
solana_program_test::*, | ||
solana_sdk::{ | ||
instruction::InstructionError, | ||
pubkey::Pubkey, | ||
signature::{Keypair, Signer}, | ||
transaction::Transaction, | ||
}, | ||
}; | ||
|
||
mod common; | ||
|
||
#[tokio::test] | ||
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(); | ||
add_lookup_table_account(&mut context, lookup_table_address, initialized_table).await; | ||
|
||
let client = &mut context.banks_client; | ||
let payer = &context.payer; | ||
let recent_blockhash = context.last_blockhash; | ||
let transaction = Transaction::new_signed_with_payer( | ||
&[close_lookup_table( | ||
lookup_table_address, | ||
authority_keypair.pubkey(), | ||
context.payer.pubkey(), | ||
)], | ||
Some(&payer.pubkey()), | ||
&[payer, &authority_keypair], | ||
recent_blockhash, | ||
); | ||
|
||
assert_matches!(client.process_transaction(transaction).await, Ok(())); | ||
assert!(client | ||
.get_account(lookup_table_address) | ||
.await | ||
.unwrap() | ||
.is_none()); | ||
} | ||
|
||
#[tokio::test] | ||
async fn test_close_lookup_table_too_recent() { | ||
let mut context = setup_test_context().await; | ||
|
||
let authority_keypair = Keypair::new(); | ||
let initialized_table = new_address_lookup_table(Some(authority_keypair.pubkey()), 0); | ||
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. | ||
assert_ix_error( | ||
&mut context, | ||
ix, | ||
Some(&authority_keypair), | ||
InstructionError::InvalidArgument, | ||
) | ||
.await; | ||
} | ||
|
||
#[tokio::test] | ||
async fn test_close_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 = close_lookup_table( | ||
lookup_table_address, | ||
authority.pubkey(), | ||
Pubkey::new_unique(), | ||
); | ||
|
||
assert_ix_error( | ||
&mut context, | ||
ix, | ||
Some(&authority), | ||
InstructionError::Immutable, | ||
) | ||
.await; | ||
} | ||
|
||
#[tokio::test] | ||
async fn test_close_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 = close_lookup_table( | ||
lookup_table_address, | ||
wrong_authority.pubkey(), | ||
Pubkey::new_unique(), | ||
); | ||
|
||
assert_ix_error( | ||
&mut context, | ||
ix, | ||
Some(&wrong_authority), | ||
InstructionError::IncorrectAuthority, | ||
) | ||
.await; | ||
} | ||
|
||
#[tokio::test] | ||
async fn test_close_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 = close_lookup_table( | ||
lookup_table_address, | ||
authority.pubkey(), | ||
Pubkey::new_unique(), | ||
); | ||
ix.accounts[1].is_signer = false; | ||
|
||
assert_ix_error( | ||
&mut context, | ||
ix, | ||
None, | ||
InstructionError::MissingRequiredSignature, | ||
) | ||
.await; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
#![allow(dead_code)] | ||
use { | ||
solana_address_lookup_table_program::{ | ||
id, | ||
processor::process_instruction, | ||
state::{AddressLookupTable, LookupTableMeta}, | ||
}, | ||
solana_program_test::*, | ||
solana_sdk::{ | ||
account::AccountSharedData, | ||
clock::Slot, | ||
hash::Hash, | ||
instruction::Instruction, | ||
instruction::InstructionError, | ||
pubkey::Pubkey, | ||
signature::{Keypair, Signer}, | ||
slot_hashes::SlotHashes, | ||
transaction::{Transaction, TransactionError}, | ||
}, | ||
std::borrow::Cow, | ||
}; | ||
|
||
pub async fn setup_test_context() -> ProgramTestContext { | ||
let program_test = ProgramTest::new("", id(), Some(process_instruction)); | ||
program_test.start_with_context().await | ||
} | ||
|
||
pub async fn assert_ix_error( | ||
context: &mut ProgramTestContext, | ||
ix: Instruction, | ||
authority_keypair: Option<&Keypair>, | ||
expected_err: InstructionError, | ||
) { | ||
let client = &mut context.banks_client; | ||
let payer = &context.payer; | ||
let recent_blockhash = context.last_blockhash; | ||
|
||
let mut signers = vec![payer]; | ||
if let Some(authority) = authority_keypair { | ||
signers.push(authority); | ||
} | ||
|
||
let transaction = Transaction::new_signed_with_payer( | ||
&[ix], | ||
Some(&payer.pubkey()), | ||
&signers, | ||
recent_blockhash, | ||
); | ||
|
||
assert_eq!( | ||
client | ||
.process_transaction(transaction) | ||
.await | ||
.unwrap_err() | ||
.unwrap(), | ||
TransactionError::InstructionError(0, expected_err), | ||
); | ||
} | ||
|
||
pub fn new_address_lookup_table( | ||
authority: Option<Pubkey>, | ||
num_addresses: usize, | ||
) -> AddressLookupTable<'static> { | ||
let mut addresses = Vec::with_capacity(num_addresses); | ||
addresses.resize_with(num_addresses, Pubkey::new_unique); | ||
AddressLookupTable { | ||
meta: LookupTableMeta { | ||
authority, | ||
..LookupTableMeta::default() | ||
}, | ||
addresses: Cow::Owned(addresses), | ||
} | ||
} | ||
|
||
pub async fn add_lookup_table_account( | ||
context: &mut ProgramTestContext, | ||
account_address: Pubkey, | ||
address_lookup_table: AddressLookupTable<'static>, | ||
) -> AccountSharedData { | ||
let mut data = Vec::new(); | ||
address_lookup_table.serialize_for_tests(&mut data).unwrap(); | ||
|
||
let rent = context.banks_client.get_rent().await.unwrap(); | ||
let rent_exempt_balance = rent.minimum_balance(data.len()); | ||
|
||
let mut account = AccountSharedData::new( | ||
rent_exempt_balance, | ||
data.len(), | ||
&solana_address_lookup_table_program::id(), | ||
); | ||
account.set_data(data); | ||
context.set_account(&account_address, &account); | ||
|
||
account | ||
} | ||
|
||
pub fn overwrite_slot_hashes_with_slots(context: &mut ProgramTestContext, slots: &[Slot]) { | ||
let mut slot_hashes = SlotHashes::default(); | ||
for slot in slots { | ||
slot_hashes.add(*slot, Hash::new_unique()); | ||
} | ||
context.set_sysvar(&slot_hashes); | ||
} |
Oops, something went wrong.