Skip to content

Commit

Permalink
Recycles existing account structs in CPI syscall.
Browse files Browse the repository at this point in the history
  • Loading branch information
Lichtso committed Sep 16, 2021
1 parent 9a2214c commit 9357b39
Showing 1 changed file with 75 additions and 95 deletions.
170 changes: 75 additions & 95 deletions programs/bpf_loader/src/syscalls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use solana_rbpf::{
#[allow(deprecated)]
use solana_sdk::sysvar::fees::Fees;
use solana_sdk::{
account::{Account, AccountSharedData, ReadableAccount},
account::{AccountSharedData, ReadableAccount},
account_info::AccountInfo,
blake3, bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable,
clock::Clock,
Expand All @@ -27,7 +27,9 @@ use solana_sdk::{
hash::{Hasher, HASH_BYTES},
ic_msg,
instruction::{AccountMeta, Instruction, InstructionError},
keccak, native_loader,
keccak,
message::Message,
native_loader,
process_instruction::{self, stable_log, ComputeMeter, InvokeContext, Logger},
program::MAX_RETURN_DATA,
pubkey::{Pubkey, PubkeyError, MAX_SEEDS, MAX_SEED_LEN},
Expand Down Expand Up @@ -1645,8 +1647,7 @@ trait SyscallInvokeSigned<'a> {
) -> Result<Instruction, EbpfError<BpfError>>;
fn translate_accounts(
&self,
account_keys: &[Pubkey],
program_account_index: usize,
message: &Message,
account_infos_addr: u64,
account_infos_len: u64,
memory_mapping: &MemoryMapping,
Expand Down Expand Up @@ -1719,8 +1720,7 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedRust<'a> {

fn translate_accounts(
&self,
account_keys: &[Pubkey],
program_account_index: usize,
message: &Message,
account_infos_addr: u64,
account_infos_len: u64,
memory_mapping: &MemoryMapping,
Expand Down Expand Up @@ -1829,8 +1829,7 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedRust<'a> {
};

get_translated_accounts(
account_keys,
program_account_index,
message,
&account_info_keys,
account_infos,
invoke_context,
Expand Down Expand Up @@ -2042,8 +2041,7 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedC<'a> {

fn translate_accounts(
&self,
account_keys: &[Pubkey],
program_account_index: usize,
message: &Message,
account_infos_addr: u64,
account_infos_len: u64,
memory_mapping: &MemoryMapping,
Expand Down Expand Up @@ -2134,8 +2132,7 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedC<'a> {
};

get_translated_accounts(
account_keys,
program_account_index,
message,
&account_info_keys,
account_infos,
invoke_context,
Expand Down Expand Up @@ -2223,8 +2220,7 @@ impl<'a> SyscallObject<BpfError> for SyscallInvokeSignedC<'a> {
}

fn get_translated_accounts<'a, T, F>(
account_keys: &[Pubkey],
program_account_index: usize,
message: &Message,
account_info_keys: &[&Pubkey],
account_infos: &[T],
invoke_context: &mut dyn InvokeContext,
Expand All @@ -2233,53 +2229,44 @@ fn get_translated_accounts<'a, T, F>(
where
F: Fn(&T, &mut dyn InvokeContext) -> Result<AccountReferences<'a>, EbpfError<BpfError>>,
{
let mut accounts = Vec::with_capacity(account_keys.len());
let mut refs = Vec::with_capacity(account_keys.len());
for (i, ref account_key) in account_keys.iter().enumerate() {
let (_account_index, account) =
invoke_context.get_account(account_key).ok_or_else(|| {
let demote_program_write_locks =
invoke_context.is_feature_active(&demote_program_write_locks::id());
let mut accounts = Vec::with_capacity(message.account_keys.len());
let mut refs = Vec::with_capacity(message.account_keys.len());
for (i, account_key) in message.account_keys.iter().enumerate() {
let ((_account_index, account), account_ref_index) = invoke_context
.get_account(account_key)
.zip(account_info_keys.iter().position(|key| *key == account_key))
.ok_or_else(|| {
ic_msg!(
invoke_context,
"Instruction references an unknown account {}",
account_key
);
SyscallError::InstructionError(InstructionError::MissingAccount)
})?;
let account_ref = do_translate(&account_infos[account_ref_index], invoke_context)?;

if i == program_account_index || account.borrow().executable() {
// Use the known account
accounts.push((**account_key, account));
refs.push(None);
} else if let Some(account_info) =
account_info_keys
.iter()
.zip(account_infos)
.find_map(|(key, account_info)| {
if key == account_key {
Some(account_info)
} else {
None
}
})
{
let account_ref = do_translate(account_info, invoke_context)?;
let account = Rc::new(RefCell::new(AccountSharedData::from(Account {
lamports: *account_ref.lamports,
data: account_ref.data.to_vec(),
executable: account_ref.executable,
owner: *account_ref.owner,
rent_epoch: account_ref.rent_epoch,
})));
accounts.push((**account_key, account));
refs.push(Some(account_ref));
} else {
ic_msg!(
invoke_context,
"Instruction references an unknown account {}",
account_key
);
return Err(SyscallError::InstructionError(InstructionError::MissingAccount).into());
let mut account = account.borrow_mut();
use solana_sdk::account::WritableAccount;
if !account.executable() {
account.copy_into_owner_from_slice(account_ref.owner.as_ref());
account.set_data_from_slice(account_ref.data);
account.set_lamports(*account_ref.lamports);
account.set_executable(account_ref.executable);
account.set_rent_epoch(account_ref.rent_epoch);
}
}
accounts.push((*account_key, account));

refs.push(
if !message.is_writable(i, demote_program_write_locks) || account_ref.executable {
None
} else {
Some(account_ref)
},
);
}

Ok((accounts, refs))
Expand Down Expand Up @@ -2374,8 +2361,7 @@ fn call<'a>(
invoke_context.is_feature_active(&close_upgradeable_program_accounts::id()),
)?;
let (accounts, account_refs) = syscall.translate_accounts(
&message.account_keys,
message.instructions[0].program_id_index as usize,
&message,
account_infos_addr,
account_infos_len,
memory_mapping,
Expand All @@ -2396,52 +2382,46 @@ fn call<'a>(
.map_err(SyscallError::InstructionError)?;

// Copy results back to caller
let demote_program_write_locks =
invoke_context.is_feature_active(&demote_program_write_locks::id());
for (i, ((_key, account), account_ref)) in accounts.iter().zip(account_refs).enumerate() {
for ((_key, account), account_ref) in accounts.iter().zip(account_refs) {
let account = account.borrow();
if let Some(mut account_ref) = account_ref {
if message.is_writable(i, demote_program_write_locks) && !account.executable() {
*account_ref.lamports = account.lamports();
*account_ref.owner = *account.owner();
if account_ref.data.len() != account.data().len() {
if !account_ref.data.is_empty() {
// Only support for `CreateAccount` at this time.
// Need a way to limit total realloc size across multiple CPI calls
ic_msg!(
invoke_context,
"Inner instructions do not support realloc, only SystemProgram::CreateAccount",
);
return Err(SyscallError::InstructionError(
InstructionError::InvalidRealloc,
)
.into());
}
if account.data().len() > account_ref.data.len() + MAX_PERMITTED_DATA_INCREASE {
ic_msg!(
invoke_context,
"SystemProgram::CreateAccount data size limited to {} in inner instructions",
MAX_PERMITTED_DATA_INCREASE
);
return Err(SyscallError::InstructionError(
InstructionError::InvalidRealloc,
)
.into());
}
account_ref.data = translate_slice_mut::<u8>(
memory_mapping,
account_ref.vm_data_addr,
account.data().len() as u64,
&bpf_loader_deprecated::id(), // Don't care since it is byte aligned
true,
)?;
*account_ref.ref_to_len_in_vm = account.data().len() as u64;
*account_ref.serialized_len_ptr = account.data().len() as u64;
*account_ref.lamports = account.lamports();
*account_ref.owner = *account.owner();
if account_ref.data.len() != account.data().len() {
if !account_ref.data.is_empty() {
// Only support for `CreateAccount` at this time.
// Need a way to limit total realloc size across multiple CPI calls
ic_msg!(
invoke_context,
"Inner instructions do not support realloc, only SystemProgram::CreateAccount",
);
return Err(
SyscallError::InstructionError(InstructionError::InvalidRealloc).into(),
);
}
account_ref
.data
.copy_from_slice(&account.data()[0..account_ref.data.len()]);
if account.data().len() > account_ref.data.len() + MAX_PERMITTED_DATA_INCREASE {
ic_msg!(
invoke_context,
"SystemProgram::CreateAccount data size limited to {} in inner instructions",
MAX_PERMITTED_DATA_INCREASE
);
return Err(
SyscallError::InstructionError(InstructionError::InvalidRealloc).into(),
);
}
account_ref.data = translate_slice_mut::<u8>(
memory_mapping,
account_ref.vm_data_addr,
account.data().len() as u64,
&bpf_loader_deprecated::id(), // Don't care since it is byte aligned
true,
)?;
*account_ref.ref_to_len_in_vm = account.data().len() as u64;
*account_ref.serialized_len_ptr = account.data().len() as u64;
}
account_ref
.data
.copy_from_slice(&account.data()[0..account_ref.data.len()]);
}
}

Expand Down

0 comments on commit 9357b39

Please sign in to comment.