Remove KeyedAccount in builtin program "address lookup table" (#24283)

* Makes sure that there is only one KeyedAccount at a time.

* KeyedAccount by BorrowedAccount in address_lookup_table.

* Cleanup unused code.
This commit is contained in:
Alexander Meißner 2022-04-13 12:17:07 +02:00 committed by GitHub
parent b8ca1bcb68
commit 096febd593
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 112 additions and 98 deletions

View File

@ -8,11 +8,8 @@ use {
}, },
solana_program_runtime::{ic_msg, invoke_context::InvokeContext}, solana_program_runtime::{ic_msg, invoke_context::InvokeContext},
solana_sdk::{ solana_sdk::{
account::{ReadableAccount, WritableAccount},
account_utils::State,
clock::Slot, clock::Slot,
instruction::InstructionError, instruction::InstructionError,
keyed_account::keyed_account_at_index,
program_utils::limited_deserialize, program_utils::limited_deserialize,
pubkey::{Pubkey, PUBKEY_BYTES}, pubkey::{Pubkey, PUBKEY_BYTES},
system_instruction, system_instruction,
@ -60,34 +57,40 @@ pub struct Processor;
impl Processor { impl Processor {
fn create_lookup_table( fn create_lookup_table(
invoke_context: &mut InvokeContext, invoke_context: &mut InvokeContext,
first_instruction_account: usize, _first_instruction_account: usize,
untrusted_recent_slot: Slot, untrusted_recent_slot: Slot,
bump_seed: u8, bump_seed: u8,
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
let transaction_context = &invoke_context.transaction_context; let transaction_context = &invoke_context.transaction_context;
let _instruction_context = transaction_context.get_current_instruction_context()?; let instruction_context = transaction_context.get_current_instruction_context()?;
let keyed_accounts = invoke_context.get_keyed_accounts()?;
let lookup_table_account = let lookup_table_account =
keyed_account_at_index(keyed_accounts, first_instruction_account)?; instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
if lookup_table_account.data_len()? > 0 { let lookup_table_lamports = lookup_table_account.get_lamports();
let table_key = *lookup_table_account.get_key();
if !lookup_table_account.get_data().is_empty() {
ic_msg!(invoke_context, "Table account must not be allocated"); ic_msg!(invoke_context, "Table account must not be allocated");
return Err(InstructionError::AccountAlreadyInitialized); return Err(InstructionError::AccountAlreadyInitialized);
} }
drop(lookup_table_account);
let authority_account = let authority_account =
keyed_account_at_index(keyed_accounts, checked_add(first_instruction_account, 1)?)?; instruction_context.try_borrow_instruction_account(transaction_context, 1)?;
let authority_key = *authority_account.signer_key().ok_or_else(|| { let authority_key = *authority_account.get_key();
if !authority_account.is_signer() {
ic_msg!(invoke_context, "Authority account must be a signer"); ic_msg!(invoke_context, "Authority account must be a signer");
InstructionError::MissingRequiredSignature return Err(InstructionError::MissingRequiredSignature);
})?; }
drop(authority_account);
let payer_account = let payer_account =
keyed_account_at_index(keyed_accounts, checked_add(first_instruction_account, 2)?)?; instruction_context.try_borrow_instruction_account(transaction_context, 2)?;
let payer_key = *payer_account.signer_key().ok_or_else(|| { let payer_key = *payer_account.get_key();
if !payer_account.is_signer() {
ic_msg!(invoke_context, "Payer account must be a signer"); ic_msg!(invoke_context, "Payer account must be a signer");
InstructionError::MissingRequiredSignature return Err(InstructionError::MissingRequiredSignature);
})?; }
drop(payer_account);
let derivation_slot = { let derivation_slot = {
let slot_hashes = invoke_context.get_sysvar_cache().get_slot_hashes()?; let slot_hashes = invoke_context.get_sysvar_cache().get_slot_hashes()?;
@ -114,7 +117,6 @@ impl Processor {
&crate::id(), &crate::id(),
)?; )?;
let table_key = *lookup_table_account.unsigned_key();
if table_key != derived_table_key { if table_key != derived_table_key {
ic_msg!( ic_msg!(
invoke_context, invoke_context,
@ -129,7 +131,7 @@ impl Processor {
let required_lamports = rent let required_lamports = rent
.minimum_balance(table_account_data_len) .minimum_balance(table_account_data_len)
.max(1) .max(1)
.saturating_sub(lookup_table_account.lamports()?); .saturating_sub(lookup_table_lamports);
if required_lamports > 0 { if required_lamports > 0 {
invoke_context.native_invoke( invoke_context.native_invoke(
@ -148,9 +150,10 @@ impl Processor {
&[table_key], &[table_key],
)?; )?;
let keyed_accounts = invoke_context.get_keyed_accounts()?; let transaction_context = &invoke_context.transaction_context;
let lookup_table_account = let instruction_context = transaction_context.get_current_instruction_context()?;
keyed_account_at_index(keyed_accounts, first_instruction_account)?; let mut lookup_table_account =
instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
lookup_table_account.set_state(&ProgramState::LookupTable(LookupTableMeta::new( lookup_table_account.set_state(&ProgramState::LookupTable(LookupTableMeta::new(
authority_key, authority_key,
)))?; )))?;
@ -160,33 +163,37 @@ impl Processor {
fn freeze_lookup_table( fn freeze_lookup_table(
invoke_context: &mut InvokeContext, invoke_context: &mut InvokeContext,
first_instruction_account: usize, _first_instruction_account: usize,
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
let transaction_context = &invoke_context.transaction_context; let transaction_context = &invoke_context.transaction_context;
let _instruction_context = transaction_context.get_current_instruction_context()?; let instruction_context = transaction_context.get_current_instruction_context()?;
let keyed_accounts = invoke_context.get_keyed_accounts()?;
let lookup_table_account = let lookup_table_account =
keyed_account_at_index(keyed_accounts, first_instruction_account)?; instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
if lookup_table_account.owner()? != crate::id() { if *lookup_table_account.get_owner() != crate::id() {
return Err(InstructionError::InvalidAccountOwner); return Err(InstructionError::InvalidAccountOwner);
} }
drop(lookup_table_account);
let authority_account = let authority_account =
keyed_account_at_index(keyed_accounts, checked_add(first_instruction_account, 1)?)?; instruction_context.try_borrow_instruction_account(transaction_context, 1)?;
if authority_account.signer_key().is_none() { let authority_key = *authority_account.get_key();
if !authority_account.is_signer() {
ic_msg!(invoke_context, "Authority account must be a signer");
return Err(InstructionError::MissingRequiredSignature); return Err(InstructionError::MissingRequiredSignature);
} }
drop(authority_account);
let lookup_table_account_ref = lookup_table_account.try_account_ref()?; let mut lookup_table_account =
let lookup_table_data = lookup_table_account_ref.data(); instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
let lookup_table_data = lookup_table_account.get_data();
let lookup_table = AddressLookupTable::deserialize(lookup_table_data)?; let lookup_table = AddressLookupTable::deserialize(lookup_table_data)?;
if lookup_table.meta.authority.is_none() { if lookup_table.meta.authority.is_none() {
ic_msg!(invoke_context, "Lookup table is already frozen"); ic_msg!(invoke_context, "Lookup table is already frozen");
return Err(InstructionError::Immutable); return Err(InstructionError::Immutable);
} }
if lookup_table.meta.authority != Some(*authority_account.unsigned_key()) { if lookup_table.meta.authority != Some(authority_key) {
return Err(InstructionError::IncorrectAuthority); return Err(InstructionError::IncorrectAuthority);
} }
if lookup_table.meta.deactivation_slot != Slot::MAX { if lookup_table.meta.deactivation_slot != Slot::MAX {
@ -199,13 +206,9 @@ impl Processor {
} }
let mut lookup_table_meta = lookup_table.meta; let mut lookup_table_meta = lookup_table.meta;
drop(lookup_table_account_ref);
lookup_table_meta.authority = None; lookup_table_meta.authority = None;
AddressLookupTable::overwrite_meta_data( AddressLookupTable::overwrite_meta_data(
lookup_table_account lookup_table_account.get_data_mut(),
.try_account_ref_mut()?
.data_as_mut_slice(),
lookup_table_meta, lookup_table_meta,
)?; )?;
@ -214,33 +217,39 @@ impl Processor {
fn extend_lookup_table( fn extend_lookup_table(
invoke_context: &mut InvokeContext, invoke_context: &mut InvokeContext,
first_instruction_account: usize, _first_instruction_account: usize,
new_addresses: Vec<Pubkey>, new_addresses: Vec<Pubkey>,
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
let transaction_context = &invoke_context.transaction_context; let transaction_context = &invoke_context.transaction_context;
let _instruction_context = transaction_context.get_current_instruction_context()?; let instruction_context = transaction_context.get_current_instruction_context()?;
let keyed_accounts = invoke_context.get_keyed_accounts()?;
let lookup_table_account = let lookup_table_account =
keyed_account_at_index(keyed_accounts, first_instruction_account)?; instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
if lookup_table_account.owner()? != crate::id() { let table_key = *lookup_table_account.get_key();
if *lookup_table_account.get_owner() != crate::id() {
return Err(InstructionError::InvalidAccountOwner); return Err(InstructionError::InvalidAccountOwner);
} }
drop(lookup_table_account);
let authority_account = let authority_account =
keyed_account_at_index(keyed_accounts, checked_add(first_instruction_account, 1)?)?; instruction_context.try_borrow_instruction_account(transaction_context, 1)?;
if authority_account.signer_key().is_none() { let authority_key = *authority_account.get_key();
if !authority_account.is_signer() {
ic_msg!(invoke_context, "Authority account must be a signer");
return Err(InstructionError::MissingRequiredSignature); return Err(InstructionError::MissingRequiredSignature);
} }
drop(authority_account);
let lookup_table_account_ref = lookup_table_account.try_account_ref()?; let mut lookup_table_account =
let lookup_table_data = lookup_table_account_ref.data(); instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
let lookup_table_data = lookup_table_account.get_data();
let lookup_table_lamports = lookup_table_account.get_lamports();
let mut lookup_table = AddressLookupTable::deserialize(lookup_table_data)?; let mut lookup_table = AddressLookupTable::deserialize(lookup_table_data)?;
if lookup_table.meta.authority.is_none() { if lookup_table.meta.authority.is_none() {
return Err(InstructionError::Immutable); return Err(InstructionError::Immutable);
} }
if lookup_table.meta.authority != Some(*authority_account.unsigned_key()) { if lookup_table.meta.authority != Some(authority_key) {
return Err(InstructionError::IncorrectAuthority); return Err(InstructionError::IncorrectAuthority);
} }
if lookup_table.meta.deactivation_slot != Slot::MAX { if lookup_table.meta.deactivation_slot != Slot::MAX {
@ -286,42 +295,36 @@ impl Processor {
} }
let lookup_table_meta = lookup_table.meta; let lookup_table_meta = lookup_table.meta;
drop(lookup_table_account_ref);
let new_table_data_len = checked_add( let new_table_data_len = checked_add(
LOOKUP_TABLE_META_SIZE, LOOKUP_TABLE_META_SIZE,
new_table_addresses_len.saturating_mul(PUBKEY_BYTES), new_table_addresses_len.saturating_mul(PUBKEY_BYTES),
)?; )?;
{ {
let mut lookup_table_account_ref_mut = lookup_table_account.try_account_ref_mut()?; let mut table_data = lookup_table_account.get_data_mut().to_vec();
AddressLookupTable::overwrite_meta_data( AddressLookupTable::overwrite_meta_data(&mut table_data, lookup_table_meta)?;
lookup_table_account_ref_mut.data_as_mut_slice(),
lookup_table_meta,
)?;
let table_data = lookup_table_account_ref_mut.data_mut();
for new_address in new_addresses { for new_address in new_addresses {
table_data.extend_from_slice(new_address.as_ref()); table_data.extend_from_slice(new_address.as_ref());
} }
lookup_table_account.set_data(&table_data);
} }
drop(lookup_table_account);
let rent = invoke_context.get_sysvar_cache().get_rent()?; let rent = invoke_context.get_sysvar_cache().get_rent()?;
let required_lamports = rent let required_lamports = rent
.minimum_balance(new_table_data_len) .minimum_balance(new_table_data_len)
.max(1) .max(1)
.saturating_sub(lookup_table_account.lamports()?); .saturating_sub(lookup_table_lamports);
let table_key = *lookup_table_account.unsigned_key();
if required_lamports > 0 { if required_lamports > 0 {
let payer_account = let payer_account =
keyed_account_at_index(keyed_accounts, checked_add(first_instruction_account, 2)?)?; instruction_context.try_borrow_instruction_account(transaction_context, 2)?;
let payer_key = if let Some(payer_key) = payer_account.signer_key() { let payer_key = *payer_account.get_key();
*payer_key if !payer_account.is_signer() {
} else {
ic_msg!(invoke_context, "Payer account must be a signer"); ic_msg!(invoke_context, "Payer account must be a signer");
return Err(InstructionError::MissingRequiredSignature); return Err(InstructionError::MissingRequiredSignature);
}; }
drop(payer_account);
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),
@ -334,33 +337,37 @@ impl Processor {
fn deactivate_lookup_table( fn deactivate_lookup_table(
invoke_context: &mut InvokeContext, invoke_context: &mut InvokeContext,
first_instruction_account: usize, _first_instruction_account: usize,
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
let transaction_context = &invoke_context.transaction_context; let transaction_context = &invoke_context.transaction_context;
let _instruction_context = transaction_context.get_current_instruction_context()?; let instruction_context = transaction_context.get_current_instruction_context()?;
let keyed_accounts = invoke_context.get_keyed_accounts()?;
let lookup_table_account = let lookup_table_account =
keyed_account_at_index(keyed_accounts, first_instruction_account)?; instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
if lookup_table_account.owner()? != crate::id() { if *lookup_table_account.get_owner() != crate::id() {
return Err(InstructionError::InvalidAccountOwner); return Err(InstructionError::InvalidAccountOwner);
} }
drop(lookup_table_account);
let authority_account = let authority_account =
keyed_account_at_index(keyed_accounts, checked_add(first_instruction_account, 1)?)?; instruction_context.try_borrow_instruction_account(transaction_context, 1)?;
if authority_account.signer_key().is_none() { let authority_key = *authority_account.get_key();
if !authority_account.is_signer() {
ic_msg!(invoke_context, "Authority account must be a signer");
return Err(InstructionError::MissingRequiredSignature); return Err(InstructionError::MissingRequiredSignature);
} }
drop(authority_account);
let lookup_table_account_ref = lookup_table_account.try_account_ref()?; let mut lookup_table_account =
let lookup_table_data = lookup_table_account_ref.data(); instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
let lookup_table_data = lookup_table_account.get_data();
let lookup_table = AddressLookupTable::deserialize(lookup_table_data)?; let lookup_table = AddressLookupTable::deserialize(lookup_table_data)?;
if lookup_table.meta.authority.is_none() { if lookup_table.meta.authority.is_none() {
ic_msg!(invoke_context, "Lookup table is frozen"); ic_msg!(invoke_context, "Lookup table is frozen");
return Err(InstructionError::Immutable); return Err(InstructionError::Immutable);
} }
if lookup_table.meta.authority != Some(*authority_account.unsigned_key()) { if lookup_table.meta.authority != Some(authority_key) {
return Err(InstructionError::IncorrectAuthority); return Err(InstructionError::IncorrectAuthority);
} }
if lookup_table.meta.deactivation_slot != Slot::MAX { if lookup_table.meta.deactivation_slot != Slot::MAX {
@ -369,15 +376,11 @@ impl Processor {
} }
let mut lookup_table_meta = lookup_table.meta; let mut lookup_table_meta = lookup_table.meta;
drop(lookup_table_account_ref);
let clock = invoke_context.get_sysvar_cache().get_clock()?; let clock = invoke_context.get_sysvar_cache().get_clock()?;
lookup_table_meta.deactivation_slot = clock.slot; lookup_table_meta.deactivation_slot = clock.slot;
AddressLookupTable::overwrite_meta_data( AddressLookupTable::overwrite_meta_data(
lookup_table_account lookup_table_account.get_data_mut(),
.try_account_ref_mut()?
.data_as_mut_slice(),
lookup_table_meta, lookup_table_meta,
)?; )?;
@ -386,27 +389,36 @@ impl Processor {
fn close_lookup_table( fn close_lookup_table(
invoke_context: &mut InvokeContext, invoke_context: &mut InvokeContext,
first_instruction_account: usize, _first_instruction_account: usize,
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
let transaction_context = &invoke_context.transaction_context; let transaction_context = &invoke_context.transaction_context;
let _instruction_context = transaction_context.get_current_instruction_context()?; let instruction_context = transaction_context.get_current_instruction_context()?;
let keyed_accounts = invoke_context.get_keyed_accounts()?;
let lookup_table_account = let lookup_table_account =
keyed_account_at_index(keyed_accounts, first_instruction_account)?; instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
if lookup_table_account.owner()? != crate::id() { if *lookup_table_account.get_owner() != crate::id() {
return Err(InstructionError::InvalidAccountOwner); return Err(InstructionError::InvalidAccountOwner);
} }
drop(lookup_table_account);
let authority_account = let authority_account =
keyed_account_at_index(keyed_accounts, checked_add(first_instruction_account, 1)?)?; instruction_context.try_borrow_instruction_account(transaction_context, 1)?;
if authority_account.signer_key().is_none() { let authority_key = *authority_account.get_key();
if !authority_account.is_signer() {
ic_msg!(invoke_context, "Authority account must be a signer");
return Err(InstructionError::MissingRequiredSignature); return Err(InstructionError::MissingRequiredSignature);
} }
drop(authority_account);
let recipient_account = instruction_context.check_number_of_instruction_accounts(3)?;
keyed_account_at_index(keyed_accounts, checked_add(first_instruction_account, 2)?)?; if instruction_context
if recipient_account.unsigned_key() == lookup_table_account.unsigned_key() { .get_index_in_transaction(instruction_context.get_number_of_program_accounts())?
== instruction_context.get_index_in_transaction(
instruction_context
.get_number_of_program_accounts()
.saturating_add(2),
)?
{
ic_msg!( ic_msg!(
invoke_context, invoke_context,
"Lookup table cannot be the recipient of reclaimed lamports" "Lookup table cannot be the recipient of reclaimed lamports"
@ -414,15 +426,17 @@ impl Processor {
return Err(InstructionError::InvalidArgument); return Err(InstructionError::InvalidArgument);
} }
let lookup_table_account_ref = lookup_table_account.try_account_ref()?; let lookup_table_account =
let lookup_table_data = lookup_table_account_ref.data(); instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
let withdrawn_lamports = lookup_table_account.get_lamports();
let lookup_table_data = lookup_table_account.get_data();
let lookup_table = AddressLookupTable::deserialize(lookup_table_data)?; let lookup_table = AddressLookupTable::deserialize(lookup_table_data)?;
if lookup_table.meta.authority.is_none() { if lookup_table.meta.authority.is_none() {
ic_msg!(invoke_context, "Lookup table is frozen"); ic_msg!(invoke_context, "Lookup table is frozen");
return Err(InstructionError::Immutable); return Err(InstructionError::Immutable);
} }
if lookup_table.meta.authority != Some(*authority_account.unsigned_key()) { if lookup_table.meta.authority != Some(authority_key) {
return Err(InstructionError::IncorrectAuthority); return Err(InstructionError::IncorrectAuthority);
} }
@ -445,16 +459,16 @@ impl Processor {
} }
LookupTableStatus::Deactivated => Ok(()), LookupTableStatus::Deactivated => Ok(()),
}?; }?;
drop(lookup_table_account);
drop(lookup_table_account_ref); let mut recipient_account =
instruction_context.try_borrow_instruction_account(transaction_context, 2)?;
recipient_account.checked_add_lamports(withdrawn_lamports)?;
drop(recipient_account);
let withdrawn_lamports = lookup_table_account.lamports()?; let mut lookup_table_account =
recipient_account instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
.try_account_ref_mut()? lookup_table_account.set_data(&[]);
.checked_add_lamports(withdrawn_lamports)?;
let mut lookup_table_account = lookup_table_account.try_account_ref_mut()?;
lookup_table_account.set_data(Vec::new());
lookup_table_account.set_lamports(0); lookup_table_account.set_lamports(0);
Ok(()) Ok(())