CPI without Account Refs (#20034)
* Removes search for accounts and unsafe lifetime transmute in InvokeContext::push(). * Replaces accounts by account_indices in verify_and_update() and process_cross_program_instruction(). Co-authored-by: Justin Starry <justin.m.starry@gmail.com>
This commit is contained in:
committed by
GitHub
parent
f128cf69a5
commit
b507715d44
@ -5,10 +5,10 @@ use solana_sdk::{
|
|||||||
account_utils::StateMut,
|
account_utils::StateMut,
|
||||||
bpf_loader_upgradeable::{self, UpgradeableLoaderState},
|
bpf_loader_upgradeable::{self, UpgradeableLoaderState},
|
||||||
feature_set::{demote_program_write_locks, fix_write_privs},
|
feature_set::{demote_program_write_locks, fix_write_privs},
|
||||||
ic_logger_msg, ic_msg,
|
ic_msg,
|
||||||
instruction::{CompiledInstruction, Instruction, InstructionError},
|
instruction::{Instruction, InstructionError},
|
||||||
message::Message,
|
message::Message,
|
||||||
process_instruction::{Executor, InvokeContext, Logger, ProcessInstructionWithContext},
|
process_instruction::{Executor, InvokeContext, ProcessInstructionWithContext},
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
rent::Rent,
|
rent::Rent,
|
||||||
system_program,
|
system_program,
|
||||||
@ -516,20 +516,16 @@ impl InstructionProcessor {
|
|||||||
caller_write_privileges.push(caller_keyed_accounts[*index].is_writable());
|
caller_write_privileges.push(caller_keyed_accounts[*index].is_writable());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let accounts = message
|
let mut account_indices = Vec::with_capacity(message.account_keys.len());
|
||||||
.account_keys
|
let mut accounts = Vec::with_capacity(message.account_keys.len());
|
||||||
.iter()
|
for account_key in message.account_keys.iter() {
|
||||||
.map(|account_key| {
|
let (account_index, account) = invoke_context
|
||||||
invoke_context
|
.get_account(account_key)
|
||||||
.get_account(account_key)
|
.ok_or(InstructionError::MissingAccount)?;
|
||||||
.ok_or(InstructionError::MissingAccount)
|
let account_length = account.borrow().data().len();
|
||||||
.map(|(_account_index, account)| (*account_key, account))
|
account_indices.push(account_index);
|
||||||
})
|
accounts.push((account, account_length));
|
||||||
.collect::<Result<Vec<_>, InstructionError>>()?;
|
}
|
||||||
let account_sizes = accounts
|
|
||||||
.iter()
|
|
||||||
.map(|(_key, account)| account.borrow().data().len())
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
// Record the instruction
|
// Record the instruction
|
||||||
invoke_context.record_instruction(&instruction);
|
invoke_context.record_instruction(&instruction);
|
||||||
@ -538,13 +534,13 @@ impl InstructionProcessor {
|
|||||||
InstructionProcessor::process_cross_program_instruction(
|
InstructionProcessor::process_cross_program_instruction(
|
||||||
&message,
|
&message,
|
||||||
&program_indices,
|
&program_indices,
|
||||||
&accounts,
|
&account_indices,
|
||||||
&caller_write_privileges,
|
&caller_write_privileges,
|
||||||
*invoke_context,
|
*invoke_context,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Verify the called program has not misbehaved
|
// Verify the called program has not misbehaved
|
||||||
for ((_key, account), prev_size) in accounts.iter().zip(account_sizes.iter()) {
|
for (account, prev_size) in accounts.iter() {
|
||||||
if *prev_size != account.borrow().data().len() && *prev_size != 0 {
|
if *prev_size != account.borrow().data().len() && *prev_size != 0 {
|
||||||
// Only support for `CreateAccount` at this time.
|
// Only support for `CreateAccount` at this time.
|
||||||
// Need a way to limit total realloc size across multiple CPI calls
|
// Need a way to limit total realloc size across multiple CPI calls
|
||||||
@ -564,7 +560,7 @@ impl InstructionProcessor {
|
|||||||
pub fn process_cross_program_instruction(
|
pub fn process_cross_program_instruction(
|
||||||
message: &Message,
|
message: &Message,
|
||||||
program_indices: &[usize],
|
program_indices: &[usize],
|
||||||
accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)],
|
account_indices: &[usize],
|
||||||
caller_write_privileges: &[bool],
|
caller_write_privileges: &[bool],
|
||||||
invoke_context: &mut dyn InvokeContext,
|
invoke_context: &mut dyn InvokeContext,
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
@ -577,13 +573,19 @@ impl InstructionProcessor {
|
|||||||
let program_id = instruction.program_id(&message.account_keys);
|
let program_id = instruction.program_id(&message.account_keys);
|
||||||
|
|
||||||
// Verify the calling program hasn't misbehaved
|
// Verify the calling program hasn't misbehaved
|
||||||
invoke_context.verify_and_update(instruction, accounts, caller_write_privileges)?;
|
invoke_context.verify_and_update(instruction, account_indices, caller_write_privileges)?;
|
||||||
|
|
||||||
// clear the return data
|
// clear the return data
|
||||||
invoke_context.set_return_data(None);
|
invoke_context.set_return_data(None);
|
||||||
|
|
||||||
// Invoke callee
|
// Invoke callee
|
||||||
invoke_context.push(program_id, message, instruction, program_indices, accounts)?;
|
invoke_context.push(
|
||||||
|
program_id,
|
||||||
|
message,
|
||||||
|
instruction,
|
||||||
|
program_indices,
|
||||||
|
account_indices,
|
||||||
|
)?;
|
||||||
|
|
||||||
let mut instruction_processor = InstructionProcessor::default();
|
let mut instruction_processor = InstructionProcessor::default();
|
||||||
for (program_id, process_instruction) in invoke_context.get_programs().iter() {
|
for (program_id, process_instruction) in invoke_context.get_programs().iter() {
|
||||||
@ -602,67 +604,14 @@ impl InstructionProcessor {
|
|||||||
let write_privileges: Vec<bool> = (0..message.account_keys.len())
|
let write_privileges: Vec<bool> = (0..message.account_keys.len())
|
||||||
.map(|i| message.is_writable(i, demote_program_write_locks))
|
.map(|i| message.is_writable(i, demote_program_write_locks))
|
||||||
.collect();
|
.collect();
|
||||||
result = invoke_context.verify_and_update(instruction, accounts, &write_privileges);
|
result =
|
||||||
|
invoke_context.verify_and_update(instruction, account_indices, &write_privileges);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restore previous state
|
// Restore previous state
|
||||||
invoke_context.pop();
|
invoke_context.pop();
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verify the results of a cross-program instruction
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub fn verify_and_update(
|
|
||||||
instruction: &CompiledInstruction,
|
|
||||||
pre_accounts: &mut [PreAccount],
|
|
||||||
accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)],
|
|
||||||
program_id: &Pubkey,
|
|
||||||
rent: &Rent,
|
|
||||||
write_privileges: &[bool],
|
|
||||||
timings: &mut ExecuteDetailsTimings,
|
|
||||||
logger: Rc<RefCell<dyn Logger>>,
|
|
||||||
) -> Result<(), InstructionError> {
|
|
||||||
// Verify the per-account instruction results
|
|
||||||
let (mut pre_sum, mut post_sum) = (0_u128, 0_u128);
|
|
||||||
let mut work = |_unique_index: usize, account_index: usize| {
|
|
||||||
if account_index < write_privileges.len() && account_index < accounts.len() {
|
|
||||||
let (key, account) = &accounts[account_index];
|
|
||||||
let is_writable = write_privileges[account_index];
|
|
||||||
// Find the matching PreAccount
|
|
||||||
for pre_account in pre_accounts.iter_mut() {
|
|
||||||
if key == pre_account.key() {
|
|
||||||
{
|
|
||||||
// Verify account has no outstanding references
|
|
||||||
let _ = account
|
|
||||||
.try_borrow_mut()
|
|
||||||
.map_err(|_| InstructionError::AccountBorrowOutstanding)?;
|
|
||||||
}
|
|
||||||
let account = account.borrow();
|
|
||||||
pre_account
|
|
||||||
.verify(program_id, is_writable, rent, &account, timings, false)
|
|
||||||
.map_err(|err| {
|
|
||||||
ic_logger_msg!(logger, "failed to verify account {}: {}", key, err);
|
|
||||||
err
|
|
||||||
})?;
|
|
||||||
pre_sum += u128::from(pre_account.lamports());
|
|
||||||
post_sum += u128::from(account.lamports());
|
|
||||||
if is_writable && !pre_account.executable() {
|
|
||||||
pre_account.update(&account);
|
|
||||||
}
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(InstructionError::MissingAccount)
|
|
||||||
};
|
|
||||||
instruction.visit_each_account(&mut work)?;
|
|
||||||
|
|
||||||
// Verify that the total sum of all the lamports did not change
|
|
||||||
if pre_sum != post_sum {
|
|
||||||
return Err(InstructionError::UnbalancedInstruction);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -275,30 +275,34 @@ impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs {
|
|||||||
stable_log::program_invoke(&logger, &program_id, invoke_context.invoke_depth());
|
stable_log::program_invoke(&logger, &program_id, invoke_context.invoke_depth());
|
||||||
|
|
||||||
// Convert AccountInfos into Accounts
|
// Convert AccountInfos into Accounts
|
||||||
fn ai_to_a(ai: &AccountInfo) -> AccountSharedData {
|
let mut account_indices = Vec::with_capacity(message.account_keys.len());
|
||||||
AccountSharedData::from(Account {
|
let mut accounts = Vec::with_capacity(message.account_keys.len());
|
||||||
lamports: ai.lamports(),
|
for (i, account_key) in message.account_keys.iter().enumerate() {
|
||||||
data: ai.try_borrow_data().unwrap().to_vec(),
|
let ((account_index, account), account_info) = invoke_context
|
||||||
owner: *ai.owner,
|
.get_account(account_key)
|
||||||
executable: ai.executable,
|
.zip(
|
||||||
rent_epoch: ai.rent_epoch,
|
account_infos
|
||||||
})
|
.iter()
|
||||||
}
|
.find(|account_info| account_info.unsigned_key() == account_key),
|
||||||
let mut accounts = vec![];
|
)
|
||||||
'outer: for key in &message.account_keys {
|
.ok_or(InstructionError::MissingAccount)
|
||||||
for account_info in account_infos {
|
.unwrap();
|
||||||
if account_info.unsigned_key() == key {
|
{
|
||||||
accounts.push((*key, Rc::new(RefCell::new(ai_to_a(account_info)))));
|
let mut account = account.borrow_mut();
|
||||||
continue 'outer;
|
account.copy_into_owner_from_slice(account_info.owner.as_ref());
|
||||||
}
|
account.set_data_from_slice(&account_info.try_borrow_data().unwrap());
|
||||||
|
account.set_lamports(account_info.lamports());
|
||||||
|
account.set_executable(account_info.executable);
|
||||||
|
account.set_rent_epoch(account_info.rent_epoch);
|
||||||
}
|
}
|
||||||
panic!("Account {} wasn't found in account_infos", key);
|
let account_info = if message.is_writable(i, demote_program_write_locks) {
|
||||||
|
Some(account_info)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
account_indices.push(account_index);
|
||||||
|
accounts.push((account, account_info));
|
||||||
}
|
}
|
||||||
assert_eq!(
|
|
||||||
accounts.len(),
|
|
||||||
message.account_keys.len(),
|
|
||||||
"Missing or not enough accounts passed to invoke"
|
|
||||||
);
|
|
||||||
let (program_account_index, _program_account) =
|
let (program_account_index, _program_account) =
|
||||||
invoke_context.get_account(&program_id).unwrap();
|
invoke_context.get_account(&program_id).unwrap();
|
||||||
let program_indices = vec![program_account_index];
|
let program_indices = vec![program_account_index];
|
||||||
@ -330,43 +334,37 @@ impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs {
|
|||||||
InstructionProcessor::process_cross_program_instruction(
|
InstructionProcessor::process_cross_program_instruction(
|
||||||
&message,
|
&message,
|
||||||
&program_indices,
|
&program_indices,
|
||||||
&accounts,
|
&account_indices,
|
||||||
&caller_privileges,
|
&caller_privileges,
|
||||||
invoke_context,
|
invoke_context,
|
||||||
)
|
)
|
||||||
.map_err(|err| ProgramError::try_from(err).unwrap_or_else(|err| panic!("{}", err)))?;
|
.map_err(|err| ProgramError::try_from(err).unwrap_or_else(|err| panic!("{}", err)))?;
|
||||||
|
|
||||||
// Copy writeable account modifications back into the caller's AccountInfos
|
// Copy writeable account modifications back into the caller's AccountInfos
|
||||||
for (i, (pubkey, account)) in accounts.iter().enumerate().take(message.account_keys.len()) {
|
for (account, account_info) in accounts.iter() {
|
||||||
if !message.is_writable(i, demote_program_write_locks) {
|
if let Some(account_info) = account_info {
|
||||||
continue;
|
**account_info.try_borrow_mut_lamports().unwrap() = account.borrow().lamports();
|
||||||
}
|
let mut data = account_info.try_borrow_mut_data()?;
|
||||||
for account_info in account_infos {
|
let account_borrow = account.borrow();
|
||||||
if account_info.unsigned_key() == pubkey {
|
let new_data = account_borrow.data();
|
||||||
**account_info.try_borrow_mut_lamports().unwrap() = account.borrow().lamports();
|
if account_info.owner != account.borrow().owner() {
|
||||||
|
// TODO Figure out a better way to allow the System Program to set the account owner
|
||||||
let mut data = account_info.try_borrow_mut_data()?;
|
#[allow(clippy::transmute_ptr_to_ptr)]
|
||||||
let account_borrow = account.borrow();
|
#[allow(mutable_transmutes)]
|
||||||
let new_data = account_borrow.data();
|
let account_info_mut =
|
||||||
if account_info.owner != account.borrow().owner() {
|
unsafe { transmute::<&Pubkey, &mut Pubkey>(account_info.owner) };
|
||||||
// TODO Figure out a better way to allow the System Program to set the account owner
|
*account_info_mut = *account.borrow().owner();
|
||||||
#[allow(clippy::transmute_ptr_to_ptr)]
|
|
||||||
#[allow(mutable_transmutes)]
|
|
||||||
let account_info_mut =
|
|
||||||
unsafe { transmute::<&Pubkey, &mut Pubkey>(account_info.owner) };
|
|
||||||
*account_info_mut = *account.borrow().owner();
|
|
||||||
}
|
|
||||||
if data.len() != new_data.len() {
|
|
||||||
// TODO: Figure out how to allow the System Program to resize the account data
|
|
||||||
panic!(
|
|
||||||
"Account data resizing not supported yet: {} -> {}. \
|
|
||||||
Consider making this test conditional on `#[cfg(feature = \"test-bpf\")]`",
|
|
||||||
data.len(),
|
|
||||||
new_data.len()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
data.clone_from_slice(new_data);
|
|
||||||
}
|
}
|
||||||
|
if data.len() != new_data.len() {
|
||||||
|
// TODO: Figure out how to allow the System Program to resize the account data
|
||||||
|
panic!(
|
||||||
|
"Account data resizing not supported yet: {} -> {}. \
|
||||||
|
Consider making this test conditional on `#[cfg(feature = \"test-bpf\")]`",
|
||||||
|
data.len(),
|
||||||
|
new_data.len()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
data.clone_from_slice(new_data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1513,8 +1513,11 @@ struct AccountReferences<'a> {
|
|||||||
rent_epoch: u64,
|
rent_epoch: u64,
|
||||||
}
|
}
|
||||||
type TranslatedAccounts<'a> = (
|
type TranslatedAccounts<'a> = (
|
||||||
Vec<(Pubkey, Rc<RefCell<AccountSharedData>>)>,
|
Vec<usize>,
|
||||||
Vec<Option<AccountReferences<'a>>>,
|
Vec<(
|
||||||
|
Rc<RefCell<AccountSharedData>>,
|
||||||
|
Option<AccountReferences<'a>>,
|
||||||
|
)>,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Implemented by language specific data structure translators
|
/// Implemented by language specific data structure translators
|
||||||
@ -2052,16 +2055,16 @@ where
|
|||||||
{
|
{
|
||||||
let demote_program_write_locks =
|
let demote_program_write_locks =
|
||||||
invoke_context.is_feature_active(&demote_program_write_locks::id());
|
invoke_context.is_feature_active(&demote_program_write_locks::id());
|
||||||
|
let mut account_indices = Vec::with_capacity(message.account_keys.len());
|
||||||
let mut accounts = Vec::with_capacity(message.account_keys.len());
|
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() {
|
for (i, account_key) in message.account_keys.iter().enumerate() {
|
||||||
if let Some((_account_index, account)) = invoke_context.get_account(account_key) {
|
if let Some((account_index, account)) = invoke_context.get_account(account_key) {
|
||||||
if i == message.instructions[0].program_id_index as usize
|
if i == message.instructions[0].program_id_index as usize
|
||||||
|| account.borrow().executable()
|
|| account.borrow().executable()
|
||||||
{
|
{
|
||||||
// Use the known account
|
// Use the known account
|
||||||
accounts.push((*account_key, account));
|
account_indices.push(account_index);
|
||||||
refs.push(None);
|
accounts.push((account, None));
|
||||||
continue;
|
continue;
|
||||||
} else if let Some(account_ref_index) =
|
} else if let Some(account_ref_index) =
|
||||||
account_info_keys.iter().position(|key| *key == account_key)
|
account_info_keys.iter().position(|key| *key == account_key)
|
||||||
@ -2075,12 +2078,13 @@ where
|
|||||||
account.set_executable(account_ref.executable);
|
account.set_executable(account_ref.executable);
|
||||||
account.set_rent_epoch(account_ref.rent_epoch);
|
account.set_rent_epoch(account_ref.rent_epoch);
|
||||||
}
|
}
|
||||||
accounts.push((*account_key, account));
|
let account_ref = if message.is_writable(i, demote_program_write_locks) {
|
||||||
refs.push(if message.is_writable(i, demote_program_write_locks) {
|
|
||||||
Some(account_ref)
|
Some(account_ref)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
});
|
};
|
||||||
|
account_indices.push(account_index);
|
||||||
|
accounts.push((account, account_ref));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2092,7 +2096,7 @@ where
|
|||||||
return Err(SyscallError::InstructionError(InstructionError::MissingAccount).into());
|
return Err(SyscallError::InstructionError(InstructionError::MissingAccount).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((accounts, refs))
|
Ok((account_indices, accounts))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_instruction_size(
|
fn check_instruction_size(
|
||||||
@ -2176,7 +2180,7 @@ fn call<'a>(
|
|||||||
&instruction.data,
|
&instruction.data,
|
||||||
invoke_context.is_feature_active(&close_upgradeable_program_accounts::id()),
|
invoke_context.is_feature_active(&close_upgradeable_program_accounts::id()),
|
||||||
)?;
|
)?;
|
||||||
let (accounts, account_refs) = syscall.translate_accounts(
|
let (account_indices, mut accounts) = syscall.translate_accounts(
|
||||||
&message,
|
&message,
|
||||||
account_infos_addr,
|
account_infos_addr,
|
||||||
account_infos_len,
|
account_infos_len,
|
||||||
@ -2191,16 +2195,16 @@ fn call<'a>(
|
|||||||
InstructionProcessor::process_cross_program_instruction(
|
InstructionProcessor::process_cross_program_instruction(
|
||||||
&message,
|
&message,
|
||||||
&program_indices,
|
&program_indices,
|
||||||
&accounts,
|
&account_indices,
|
||||||
&caller_write_privileges,
|
&caller_write_privileges,
|
||||||
*invoke_context,
|
*invoke_context,
|
||||||
)
|
)
|
||||||
.map_err(SyscallError::InstructionError)?;
|
.map_err(SyscallError::InstructionError)?;
|
||||||
|
|
||||||
// Copy results back to caller
|
// Copy results back to caller
|
||||||
for ((_key, account), account_ref) in accounts.iter().zip(account_refs) {
|
for (account, account_ref) in accounts.iter_mut() {
|
||||||
let account = account.borrow();
|
let account = account.borrow();
|
||||||
if let Some(mut account_ref) = account_ref {
|
if let Some(account_ref) = account_ref {
|
||||||
*account_ref.lamports = account.lamports();
|
*account_ref.lamports = account.lamports();
|
||||||
*account_ref.owner = *account.owner();
|
*account_ref.owner = *account.owner();
|
||||||
if account_ref.data.len() != account.data().len() {
|
if account_ref.data.len() != account.data().len() {
|
||||||
|
@ -119,7 +119,14 @@ impl<'a> ThisInvokeContext<'a> {
|
|||||||
fee_calculator,
|
fee_calculator,
|
||||||
return_data: None,
|
return_data: None,
|
||||||
};
|
};
|
||||||
invoke_context.push(program_id, message, instruction, program_indices, accounts)?;
|
let account_indices = (0..accounts.len()).collect::<Vec<usize>>();
|
||||||
|
invoke_context.push(
|
||||||
|
program_id,
|
||||||
|
message,
|
||||||
|
instruction,
|
||||||
|
program_indices,
|
||||||
|
&account_indices,
|
||||||
|
)?;
|
||||||
Ok(invoke_context)
|
Ok(invoke_context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -130,7 +137,7 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> {
|
|||||||
message: &Message,
|
message: &Message,
|
||||||
instruction: &CompiledInstruction,
|
instruction: &CompiledInstruction,
|
||||||
program_indices: &[usize],
|
program_indices: &[usize],
|
||||||
instruction_accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)],
|
account_indices: &[usize],
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
if self.invoke_stack.len() > self.compute_budget.max_invoke_depth {
|
if self.invoke_stack.len() > self.compute_budget.max_invoke_depth {
|
||||||
return Err(InstructionError::CallDepth);
|
return Err(InstructionError::CallDepth);
|
||||||
@ -161,44 +168,17 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> {
|
|||||||
&self.accounts[*account_index].1 as &RefCell<AccountSharedData>,
|
&self.accounts[*account_index].1 as &RefCell<AccountSharedData>,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.chain(instruction.accounts.iter().map(|index| {
|
.chain(instruction.accounts.iter().map(|index_in_instruction| {
|
||||||
let index = *index as usize;
|
let index_in_instruction = *index_in_instruction as usize;
|
||||||
|
let account_index = account_indices[index_in_instruction];
|
||||||
(
|
(
|
||||||
message.is_signer(index),
|
message.is_signer(index_in_instruction),
|
||||||
message.is_writable(index, demote_program_write_locks),
|
message.is_writable(index_in_instruction, demote_program_write_locks),
|
||||||
&instruction_accounts[index].0,
|
&self.accounts[account_index].0,
|
||||||
&instruction_accounts[index].1 as &RefCell<AccountSharedData>,
|
&self.accounts[account_index].1 as &RefCell<AccountSharedData>,
|
||||||
)
|
)
|
||||||
}))
|
}))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
// Alias the keys and account references in the provided keyed_accounts
|
|
||||||
// with the ones already existing in self, so that the lifetime 'a matches.
|
|
||||||
fn transmute_lifetime<'a, 'b, T: Sized>(value: &'a T) -> &'b T {
|
|
||||||
unsafe { std::mem::transmute(value) }
|
|
||||||
}
|
|
||||||
let keyed_accounts = keyed_accounts
|
|
||||||
.iter()
|
|
||||||
.map(|(is_signer, is_writable, search_key, account)| {
|
|
||||||
self.accounts
|
|
||||||
.iter()
|
|
||||||
.position(|(key, _account)| key == *search_key)
|
|
||||||
.map(|index| {
|
|
||||||
// TODO
|
|
||||||
// Currently we are constructing new accounts on the stack
|
|
||||||
// before calling MessageProcessor::process_cross_program_instruction
|
|
||||||
// Ideally we would recycle the existing accounts here.
|
|
||||||
(
|
|
||||||
*is_signer,
|
|
||||||
*is_writable,
|
|
||||||
&self.accounts[index].0,
|
|
||||||
// &self.accounts[index] as &RefCell<AccountSharedData>
|
|
||||||
transmute_lifetime(*account),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect::<Option<Vec<_>>>()
|
|
||||||
.ok_or(InstructionError::InvalidArgument)?;
|
|
||||||
self.invoke_stack.push(InvokeContextStackFrame::new(
|
self.invoke_stack.push(InvokeContextStackFrame::new(
|
||||||
*key,
|
*key,
|
||||||
create_keyed_accounts_unified(keyed_accounts.as_slice()),
|
create_keyed_accounts_unified(keyed_accounts.as_slice()),
|
||||||
@ -214,24 +194,63 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> {
|
|||||||
fn verify_and_update(
|
fn verify_and_update(
|
||||||
&mut self,
|
&mut self,
|
||||||
instruction: &CompiledInstruction,
|
instruction: &CompiledInstruction,
|
||||||
accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)],
|
account_indices: &[usize],
|
||||||
write_privileges: &[bool],
|
write_privileges: &[bool],
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
let stack_frame = self
|
let stack_frame = self
|
||||||
.invoke_stack
|
.invoke_stack
|
||||||
.last()
|
.last()
|
||||||
.ok_or(InstructionError::CallDepth)?;
|
.ok_or(InstructionError::CallDepth)?;
|
||||||
|
let program_id = &stack_frame.key;
|
||||||
|
let rent = &self.rent;
|
||||||
let logger = self.get_logger();
|
let logger = self.get_logger();
|
||||||
InstructionProcessor::verify_and_update(
|
let accounts = &self.accounts;
|
||||||
instruction,
|
let pre_accounts = &mut self.pre_accounts;
|
||||||
&mut self.pre_accounts,
|
let timings = &mut self.timings;
|
||||||
accounts,
|
|
||||||
&stack_frame.key,
|
// Verify the per-account instruction results
|
||||||
&self.rent,
|
let (mut pre_sum, mut post_sum) = (0_u128, 0_u128);
|
||||||
write_privileges,
|
let mut work = |_unique_index: usize, index_in_instruction: usize| {
|
||||||
&mut self.timings,
|
if index_in_instruction < write_privileges.len()
|
||||||
logger,
|
&& index_in_instruction < account_indices.len()
|
||||||
)
|
{
|
||||||
|
let account_index = account_indices[index_in_instruction];
|
||||||
|
let (key, account) = &accounts[account_index];
|
||||||
|
let is_writable = write_privileges[index_in_instruction];
|
||||||
|
// Find the matching PreAccount
|
||||||
|
for pre_account in pre_accounts.iter_mut() {
|
||||||
|
if key == pre_account.key() {
|
||||||
|
{
|
||||||
|
// Verify account has no outstanding references
|
||||||
|
let _ = account
|
||||||
|
.try_borrow_mut()
|
||||||
|
.map_err(|_| InstructionError::AccountBorrowOutstanding)?;
|
||||||
|
}
|
||||||
|
let account = account.borrow();
|
||||||
|
pre_account
|
||||||
|
.verify(program_id, is_writable, rent, &account, timings, false)
|
||||||
|
.map_err(|err| {
|
||||||
|
ic_logger_msg!(logger, "failed to verify account {}: {}", key, err);
|
||||||
|
err
|
||||||
|
})?;
|
||||||
|
pre_sum += u128::from(pre_account.lamports());
|
||||||
|
post_sum += u128::from(account.lamports());
|
||||||
|
if is_writable && !pre_account.executable() {
|
||||||
|
pre_account.update(&account);
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(InstructionError::MissingAccount)
|
||||||
|
};
|
||||||
|
instruction.visit_each_account(&mut work)?;
|
||||||
|
|
||||||
|
// Verify that the total sum of all the lamports did not change
|
||||||
|
if pre_sum != post_sum {
|
||||||
|
return Err(InstructionError::UnbalancedInstruction);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
fn get_caller(&self) -> Result<&Pubkey, InstructionError> {
|
fn get_caller(&self) -> Result<&Pubkey, InstructionError> {
|
||||||
self.invoke_stack
|
self.invoke_stack
|
||||||
@ -671,6 +690,7 @@ mod tests {
|
|||||||
));
|
));
|
||||||
metas.push(AccountMeta::new(*program_id, false));
|
metas.push(AccountMeta::new(*program_id, false));
|
||||||
}
|
}
|
||||||
|
let account_indices = (0..accounts.len()).collect::<Vec<usize>>();
|
||||||
|
|
||||||
let message = Message::new(
|
let message = Message::new(
|
||||||
&[Instruction::new_with_bytes(invoke_stack[0], &[0], metas)],
|
&[Instruction::new_with_bytes(invoke_stack[0], &[0], metas)],
|
||||||
@ -709,7 +729,7 @@ mod tests {
|
|||||||
&message,
|
&message,
|
||||||
&message.instructions[0],
|
&message.instructions[0],
|
||||||
&[],
|
&[],
|
||||||
&accounts,
|
&account_indices,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
@ -734,24 +754,19 @@ mod tests {
|
|||||||
)],
|
)],
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
|
let write_privileges: Vec<bool> = (0..message.account_keys.len())
|
||||||
|
.map(|i| message.is_writable(i, /*demote_program_write_locks=*/ true))
|
||||||
|
.collect();
|
||||||
|
|
||||||
// modify account owned by the program
|
// modify account owned by the program
|
||||||
accounts[owned_index].1.borrow_mut().data_as_mut_slice()[0] =
|
accounts[owned_index].1.borrow_mut().data_as_mut_slice()[0] =
|
||||||
(MAX_DEPTH + owned_index) as u8;
|
(MAX_DEPTH + owned_index) as u8;
|
||||||
let mut these_accounts = accounts[not_owned_index..owned_index + 1].to_vec();
|
|
||||||
these_accounts.push((
|
|
||||||
message.account_keys[2],
|
|
||||||
Rc::new(RefCell::new(AccountSharedData::new(
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
&solana_sdk::pubkey::Pubkey::default(),
|
|
||||||
))),
|
|
||||||
));
|
|
||||||
let write_privileges: Vec<bool> = (0..message.account_keys.len())
|
|
||||||
.map(|i| message.is_writable(i, /*demote_program_write_locks=*/ true))
|
|
||||||
.collect();
|
|
||||||
invoke_context
|
invoke_context
|
||||||
.verify_and_update(&message.instructions[0], &these_accounts, &write_privileges)
|
.verify_and_update(
|
||||||
|
&message.instructions[0],
|
||||||
|
&account_indices[not_owned_index..owned_index + 1],
|
||||||
|
&write_privileges,
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
invoke_context.pre_accounts[owned_index].data()[0],
|
invoke_context.pre_accounts[owned_index].data()[0],
|
||||||
@ -765,7 +780,7 @@ mod tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
invoke_context.verify_and_update(
|
invoke_context.verify_and_update(
|
||||||
&message.instructions[0],
|
&message.instructions[0],
|
||||||
&accounts[not_owned_index..owned_index + 1],
|
&account_indices[not_owned_index..owned_index + 1],
|
||||||
&write_privileges,
|
&write_privileges,
|
||||||
),
|
),
|
||||||
Err(InstructionError::ExternalAccountDataModified)
|
Err(InstructionError::ExternalAccountDataModified)
|
||||||
@ -1217,6 +1232,7 @@ mod tests {
|
|||||||
),
|
),
|
||||||
(callee_program_id, Rc::new(RefCell::new(program_account))),
|
(callee_program_id, Rc::new(RefCell::new(program_account))),
|
||||||
];
|
];
|
||||||
|
let account_indices = [0, 1, 2];
|
||||||
let program_indices = vec![3];
|
let program_indices = vec![3];
|
||||||
|
|
||||||
let programs: Vec<(_, ProcessInstructionWithContext)> =
|
let programs: Vec<(_, ProcessInstructionWithContext)> =
|
||||||
@ -1274,7 +1290,7 @@ mod tests {
|
|||||||
InstructionProcessor::process_cross_program_instruction(
|
InstructionProcessor::process_cross_program_instruction(
|
||||||
&message,
|
&message,
|
||||||
&program_indices,
|
&program_indices,
|
||||||
&accounts,
|
&account_indices,
|
||||||
&caller_write_privileges,
|
&caller_write_privileges,
|
||||||
&mut invoke_context,
|
&mut invoke_context,
|
||||||
),
|
),
|
||||||
@ -1288,7 +1304,7 @@ mod tests {
|
|||||||
InstructionProcessor::process_cross_program_instruction(
|
InstructionProcessor::process_cross_program_instruction(
|
||||||
&message,
|
&message,
|
||||||
&program_indices,
|
&program_indices,
|
||||||
&accounts,
|
&account_indices,
|
||||||
&caller_write_privileges,
|
&caller_write_privileges,
|
||||||
&mut invoke_context,
|
&mut invoke_context,
|
||||||
),
|
),
|
||||||
@ -1348,7 +1364,7 @@ mod tests {
|
|||||||
InstructionProcessor::process_cross_program_instruction(
|
InstructionProcessor::process_cross_program_instruction(
|
||||||
&message,
|
&message,
|
||||||
&program_indices,
|
&program_indices,
|
||||||
&accounts,
|
&account_indices,
|
||||||
&caller_write_privileges,
|
&caller_write_privileges,
|
||||||
&mut invoke_context,
|
&mut invoke_context,
|
||||||
),
|
),
|
||||||
|
@ -51,19 +51,15 @@ impl<'a> InvokeContextStackFrame<'a> {
|
|||||||
/// Invocation context passed to loaders
|
/// Invocation context passed to loaders
|
||||||
pub trait InvokeContext {
|
pub trait InvokeContext {
|
||||||
/// Push a stack frame onto the invocation stack
|
/// Push a stack frame onto the invocation stack
|
||||||
///
|
|
||||||
/// Used in MessageProcessor::process_cross_program_instruction
|
|
||||||
fn push(
|
fn push(
|
||||||
&mut self,
|
&mut self,
|
||||||
key: &Pubkey,
|
key: &Pubkey,
|
||||||
message: &Message,
|
message: &Message,
|
||||||
instruction: &CompiledInstruction,
|
instruction: &CompiledInstruction,
|
||||||
program_indices: &[usize],
|
program_indices: &[usize],
|
||||||
instruction_accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)],
|
account_indices: &[usize],
|
||||||
) -> Result<(), InstructionError>;
|
) -> Result<(), InstructionError>;
|
||||||
/// Pop a stack frame from the invocation stack
|
/// Pop a stack frame from the invocation stack
|
||||||
///
|
|
||||||
/// Used in MessageProcessor::process_cross_program_instruction
|
|
||||||
fn pop(&mut self);
|
fn pop(&mut self);
|
||||||
/// Current depth of the invocation stake
|
/// Current depth of the invocation stake
|
||||||
fn invoke_depth(&self) -> usize;
|
fn invoke_depth(&self) -> usize;
|
||||||
@ -71,7 +67,7 @@ pub trait InvokeContext {
|
|||||||
fn verify_and_update(
|
fn verify_and_update(
|
||||||
&mut self,
|
&mut self,
|
||||||
instruction: &CompiledInstruction,
|
instruction: &CompiledInstruction,
|
||||||
accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)],
|
account_indices: &[usize],
|
||||||
write_privileges: &[bool],
|
write_privileges: &[bool],
|
||||||
) -> Result<(), InstructionError>;
|
) -> Result<(), InstructionError>;
|
||||||
/// Get the program ID of the currently executing program
|
/// Get the program ID of the currently executing program
|
||||||
@ -478,7 +474,7 @@ impl<'a> InvokeContext for MockInvokeContext<'a> {
|
|||||||
_message: &Message,
|
_message: &Message,
|
||||||
_instruction: &CompiledInstruction,
|
_instruction: &CompiledInstruction,
|
||||||
_program_indices: &[usize],
|
_program_indices: &[usize],
|
||||||
_instruction_accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)],
|
_account_indices: &[usize],
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
self.invoke_stack.push(InvokeContextStackFrame::new(
|
self.invoke_stack.push(InvokeContextStackFrame::new(
|
||||||
*_key,
|
*_key,
|
||||||
@ -495,7 +491,7 @@ impl<'a> InvokeContext for MockInvokeContext<'a> {
|
|||||||
fn verify_and_update(
|
fn verify_and_update(
|
||||||
&mut self,
|
&mut self,
|
||||||
_instruction: &CompiledInstruction,
|
_instruction: &CompiledInstruction,
|
||||||
_accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)],
|
_account_indices: &[usize],
|
||||||
_write_pivileges: &[bool],
|
_write_pivileges: &[bool],
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
Ok(())
|
Ok(())
|
||||||
|
Reference in New Issue
Block a user