Index loaders / executable accounts (#19469)

* Appends loaders / executable_accounts to accounts in transaction loading.

* Adds indices to loaders / executable_accounts.

* Moves MessageProcessor::create_keyed_accounts() into InvokeContext::push().

* Removes "executable_accounts",
now referenced by transaction wide index into "accounts".

* Removes create_pre_accounts() from InstructionProcessor,
as it is already in MessageProcessor.

* Collect program account indices directly in load_executable_accounts().
This commit is contained in:
Alexander Meißner
2021-09-10 08:36:21 +02:00
committed by GitHub
parent 4386e09710
commit 88c1b8f047
7 changed files with 344 additions and 384 deletions

View File

@ -342,29 +342,6 @@ impl InstructionProcessor {
} }
} }
/// Create the KeyedAccounts that will be passed to the program
pub fn create_keyed_accounts<'a>(
message: &'a Message,
instruction: &'a CompiledInstruction,
executable_accounts: &'a [(Pubkey, Rc<RefCell<AccountSharedData>>)],
accounts: &'a [(Pubkey, Rc<RefCell<AccountSharedData>>)],
demote_program_write_locks: bool,
) -> Vec<(bool, bool, &'a Pubkey, &'a RefCell<AccountSharedData>)> {
executable_accounts
.iter()
.map(|(key, account)| (false, false, key, account as &RefCell<AccountSharedData>))
.chain(instruction.accounts.iter().map(|index| {
let index = *index as usize;
(
message.is_signer(index),
message.is_writable(index, demote_program_write_locks),
&accounts[index].0,
&accounts[index].1 as &RefCell<AccountSharedData>,
)
}))
.collect::<Vec<_>>()
}
/// Process an instruction /// Process an instruction
/// This method calls the instruction's program entrypoint method /// This method calls the instruction's program entrypoint method
pub fn process_instruction( pub fn process_instruction(
@ -490,7 +467,7 @@ impl InstructionProcessor {
let ( let (
message, message,
executable_accounts, program_indices,
accounts, accounts,
keyed_account_indices_reordered, keyed_account_indices_reordered,
caller_write_privileges, caller_write_privileges,
@ -535,8 +512,7 @@ impl InstructionProcessor {
invoke_context.record_instruction(&instruction); invoke_context.record_instruction(&instruction);
let program_account = let (program_account_index, program_account) = invoke_context
invoke_context
.get_account(&callee_program_id) .get_account(&callee_program_id)
.ok_or_else(|| { .ok_or_else(|| {
ic_msg!(invoke_context, "Unknown program {}", callee_program_id); ic_msg!(invoke_context, "Unknown program {}", callee_program_id);
@ -550,13 +526,16 @@ impl InstructionProcessor {
); );
return Err(InstructionError::AccountNotExecutable); return Err(InstructionError::AccountNotExecutable);
} }
let programdata = if program_account.borrow().owner() == &bpf_loader_upgradeable::id() { let mut program_indices = vec![];
if program_account.borrow().owner() == &bpf_loader_upgradeable::id() {
if let UpgradeableLoaderState::Program { if let UpgradeableLoaderState::Program {
programdata_address, programdata_address,
} = program_account.borrow().state()? } = program_account.borrow().state()?
{ {
if let Some(account) = invoke_context.get_account(&programdata_address) { if let Some((programdata_account_index, _programdata_account)) =
Some((programdata_address, account)) invoke_context.get_account(&programdata_address)
{
program_indices.push(programdata_account_index);
} else { } else {
ic_msg!( ic_msg!(
invoke_context, invoke_context,
@ -573,16 +552,11 @@ impl InstructionProcessor {
); );
return Err(InstructionError::MissingAccount); return Err(InstructionError::MissingAccount);
} }
} else {
None
};
let mut executable_accounts = vec![(callee_program_id, program_account)];
if let Some(programdata) = programdata {
executable_accounts.push(programdata);
} }
program_indices.insert(0, program_account_index);
( (
message, message,
executable_accounts, program_indices,
accounts, accounts,
keyed_account_indices_reordered, keyed_account_indices_reordered,
caller_write_privileges, caller_write_privileges,
@ -592,7 +566,7 @@ impl InstructionProcessor {
#[allow(clippy::deref_addrof)] #[allow(clippy::deref_addrof)]
InstructionProcessor::process_cross_program_instruction( InstructionProcessor::process_cross_program_instruction(
&message, &message,
&executable_accounts, &program_indices,
&accounts, &accounts,
&caller_write_privileges, &caller_write_privileges,
*(&mut *(invoke_context.borrow_mut())), *(&mut *(invoke_context.borrow_mut())),
@ -646,7 +620,7 @@ impl InstructionProcessor {
/// This method calls the instruction's program entrypoint function /// This method calls the instruction's program entrypoint function
pub fn process_cross_program_instruction( pub fn process_cross_program_instruction(
message: &Message, message: &Message,
executable_accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)], program_indices: &[usize],
accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)], accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)],
caller_write_privileges: &[bool], caller_write_privileges: &[bool],
invoke_context: &mut dyn InvokeContext, invoke_context: &mut dyn InvokeContext,
@ -657,19 +631,8 @@ impl InstructionProcessor {
// 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, accounts, caller_write_privileges)?;
// Construct keyed accounts
let demote_program_write_locks =
invoke_context.is_feature_active(&demote_program_write_locks::id());
let keyed_accounts = Self::create_keyed_accounts(
message,
instruction,
executable_accounts,
accounts,
demote_program_write_locks,
);
// Invoke callee // Invoke callee
invoke_context.push(program_id, &keyed_accounts)?; invoke_context.push(program_id, message, instruction, program_indices, accounts)?;
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() {
@ -683,6 +646,8 @@ impl InstructionProcessor {
); );
if result.is_ok() { if result.is_ok() {
// Verify the called program has not misbehaved // Verify the called program has not misbehaved
let demote_program_write_locks =
invoke_context.is_feature_active(&demote_program_write_locks::id());
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();
@ -698,28 +663,6 @@ impl InstructionProcessor {
} }
} }
/// Record the initial state of the accounts so that they can be compared
/// after the instruction is processed
pub fn create_pre_accounts(
message: &Message,
instruction: &CompiledInstruction,
accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)],
) -> Vec<PreAccount> {
let mut pre_accounts = Vec::with_capacity(instruction.accounts.len());
{
let mut work = |_unique_index: usize, account_index: usize| {
if account_index < message.account_keys.len() && account_index < accounts.len() {
let account = accounts[account_index].1.borrow();
pre_accounts.push(PreAccount::new(&accounts[account_index].0, &account));
return Ok(());
}
Err(InstructionError::MissingAccount)
};
let _ = instruction.visit_each_account(&mut work);
}
pre_accounts
}
/// Verify the results of a cross-program instruction /// Verify the results of a cross-program instruction
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn verify_and_update( pub fn verify_and_update(

View File

@ -254,14 +254,6 @@ impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs {
let message = Message::new(&[instruction.clone()], None); let message = Message::new(&[instruction.clone()], None);
let program_id_index = message.instructions[0].program_id_index as usize; let program_id_index = message.instructions[0].program_id_index as usize;
let program_id = message.account_keys[program_id_index]; let program_id = message.account_keys[program_id_index];
let program_account_info = || {
for account_info in account_infos {
if account_info.unsigned_key() == &program_id {
return account_info;
}
}
panic!("Program id {} wasn't found in account_infos", program_id);
};
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());
// TODO don't have the caller's keyed_accounts so can't validate writer or signer escalation or deescalation yet // TODO don't have the caller's keyed_accounts so can't validate writer or signer escalation or deescalation yet
@ -274,6 +266,7 @@ 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
fn ai_to_a(ai: &AccountInfo) -> AccountSharedData { fn ai_to_a(ai: &AccountInfo) -> AccountSharedData {
AccountSharedData::from(Account { AccountSharedData::from(Account {
lamports: ai.lamports(), lamports: ai.lamports(),
@ -283,12 +276,6 @@ impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs {
rent_epoch: ai.rent_epoch, rent_epoch: ai.rent_epoch,
}) })
} }
let executables = vec![(
program_id,
Rc::new(RefCell::new(ai_to_a(program_account_info()))),
)];
// Convert AccountInfos into Accounts
let mut accounts = vec![]; let mut accounts = vec![];
'outer: for key in &message.account_keys { 'outer: for key in &message.account_keys {
for account_info in account_infos { for account_info in account_infos {
@ -304,6 +291,9 @@ impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs {
message.account_keys.len(), message.account_keys.len(),
"Missing or not enough accounts passed to invoke" "Missing or not enough accounts passed to invoke"
); );
let (program_account_index, _program_account) =
invoke_context.get_account(&program_id).unwrap();
let program_indices = vec![program_account_index];
// Check Signers // Check Signers
for account_info in account_infos { for account_info in account_infos {
@ -331,7 +321,7 @@ impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs {
InstructionProcessor::process_cross_program_instruction( InstructionProcessor::process_cross_program_instruction(
&message, &message,
&executables, &program_indices,
&accounts, &accounts,
&caller_privileges, &caller_privileges,
invoke_context, invoke_context,

View File

@ -2229,7 +2229,8 @@ where
let mut accounts = Vec::with_capacity(account_keys.len()); let mut accounts = Vec::with_capacity(account_keys.len());
let mut refs = 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() { for (i, ref account_key) in account_keys.iter().enumerate() {
let account = invoke_context.get_account(account_key).ok_or_else(|| { let (_account_index, account) =
invoke_context.get_account(account_key).ok_or_else(|| {
ic_msg!( ic_msg!(
invoke_context, invoke_context,
"Instruction references an unknown account {}", "Instruction references an unknown account {}",
@ -2321,14 +2322,16 @@ fn get_upgradeable_executable(
callee_program_id: &Pubkey, callee_program_id: &Pubkey,
program_account: &Rc<RefCell<AccountSharedData>>, program_account: &Rc<RefCell<AccountSharedData>>,
invoke_context: &Ref<&mut dyn InvokeContext>, invoke_context: &Ref<&mut dyn InvokeContext>,
) -> Result<Option<(Pubkey, Rc<RefCell<AccountSharedData>>)>, EbpfError<BpfError>> { ) -> Result<Option<usize>, EbpfError<BpfError>> {
if program_account.borrow().owner() == &bpf_loader_upgradeable::id() { if program_account.borrow().owner() == &bpf_loader_upgradeable::id() {
match program_account.borrow().state() { match program_account.borrow().state() {
Ok(UpgradeableLoaderState::Program { Ok(UpgradeableLoaderState::Program {
programdata_address, programdata_address,
}) => { }) => {
if let Some(account) = invoke_context.get_account(&programdata_address) { if let Some((programdata_account_index, _programdata_account)) =
Ok(Some((programdata_address, account))) invoke_context.get_account(&programdata_address)
{
Ok(Some(programdata_account_index))
} else { } else {
ic_msg!( ic_msg!(
invoke_context, invoke_context,
@ -2364,7 +2367,7 @@ fn call<'a>(
) -> Result<u64, EbpfError<BpfError>> { ) -> Result<u64, EbpfError<BpfError>> {
let ( let (
message, message,
executables, program_indices,
accounts, accounts,
account_refs, account_refs,
caller_write_privileges, caller_write_privileges,
@ -2442,16 +2445,21 @@ fn call<'a>(
let program_account = accounts let program_account = accounts
.get(callee_program_id_index) .get(callee_program_id_index)
.ok_or_else(|| { .ok_or_else(|| {
ic_msg!(invoke_context, "Unknown program {}", callee_program_id,); ic_msg!(invoke_context, "Unknown program {}", callee_program_id);
SyscallError::InstructionError(InstructionError::MissingAccount) SyscallError::InstructionError(InstructionError::MissingAccount)
})? })?
.1 .1
.clone(); .clone();
let programdata_executable = let (program_account_index, _program_account) =
get_upgradeable_executable(&callee_program_id, &program_account, &invoke_context)?; invoke_context.get_account(&callee_program_id).ok_or(
let mut executables = vec![(callee_program_id, program_account)]; SyscallError::InstructionError(InstructionError::MissingAccount),
if let Some(executable) = programdata_executable { )?;
executables.push(executable);
let mut program_indices = vec![program_account_index];
if let Some(programdata_account_index) =
get_upgradeable_executable(&callee_program_id, &program_account, &invoke_context)?
{
program_indices.push(programdata_account_index);
} }
// Record the instruction // Record the instruction
@ -2460,7 +2468,7 @@ fn call<'a>(
( (
message, message,
executables, program_indices,
accounts, accounts,
account_refs, account_refs,
caller_write_privileges, caller_write_privileges,
@ -2473,7 +2481,7 @@ fn call<'a>(
#[allow(clippy::deref_addrof)] #[allow(clippy::deref_addrof)]
match InstructionProcessor::process_cross_program_instruction( match InstructionProcessor::process_cross_program_instruction(
&message, &message,
&executables, &program_indices,
&accounts, &accounts,
&caller_write_privileges, &caller_write_privileges,
*(&mut *(syscall.get_context_mut()?)), *(&mut *(syscall.get_context_mut()?)),

View File

@ -101,11 +101,11 @@ pub struct Accounts {
// for the load instructions // for the load instructions
pub type TransactionAccounts = Vec<(Pubkey, AccountSharedData)>; pub type TransactionAccounts = Vec<(Pubkey, AccountSharedData)>;
pub type TransactionRent = u64; pub type TransactionRent = u64;
pub type TransactionLoaders = Vec<Vec<(Pubkey, AccountSharedData)>>; pub type TransactionProgramIndices = Vec<Vec<usize>>;
#[derive(PartialEq, Debug, Clone)] #[derive(PartialEq, Debug, Clone)]
pub struct LoadedTransaction { pub struct LoadedTransaction {
pub accounts: TransactionAccounts, pub accounts: TransactionAccounts,
pub loaders: TransactionLoaders, pub program_indices: TransactionProgramIndices,
pub rent: TransactionRent, pub rent: TransactionRent,
pub rent_debits: RentDebits, pub rent_debits: RentDebits,
} }
@ -244,7 +244,10 @@ impl Accounts {
let is_upgradeable_loader_present = is_upgradeable_loader_present(message); let is_upgradeable_loader_present = is_upgradeable_loader_present(message);
for (i, key) in message.account_keys_iter().enumerate() { for (i, key) in message.account_keys_iter().enumerate() {
let account = if message.is_non_loader_key(i) { let account = if !message.is_non_loader_key(i) {
// Fill in an empty account for the program slots.
AccountSharedData::default()
} else {
if payer_index.is_none() { if payer_index.is_none() {
payer_index = Some(i); payer_index = Some(i);
} }
@ -289,12 +292,12 @@ impl Accounts {
programdata_address, programdata_address,
}) = account.state() }) = account.state()
{ {
if let Some(account) = self if let Some((programdata_account, _)) = self
.accounts_db .accounts_db
.load_with_fixed_root(ancestors, &programdata_address) .load_with_fixed_root(ancestors, &programdata_address)
.map(|(account, _)| account)
{ {
account_deps.push((programdata_address, account)); account_deps
.push((programdata_address, programdata_account));
} else { } else {
error_counters.account_not_found += 1; error_counters.account_not_found += 1;
return Err(TransactionError::ProgramAccountNotFound); return Err(TransactionError::ProgramAccountNotFound);
@ -317,9 +320,6 @@ impl Accounts {
account account
} }
} else {
// Fill in an empty account for the program slots.
AccountSharedData::default()
}; };
accounts.push((*key, account)); accounts.push((*key, account));
} }
@ -338,10 +338,9 @@ impl Accounts {
let payer_account = &mut accounts[payer_index].1; let payer_account = &mut accounts[payer_index].1;
if payer_account.lamports() == 0 { if payer_account.lamports() == 0 {
error_counters.account_not_found += 1; error_counters.account_not_found += 1;
Err(TransactionError::AccountNotFound) return Err(TransactionError::AccountNotFound);
} else { }
let min_balance = let min_balance = match get_system_account_kind(payer_account).ok_or_else(|| {
match get_system_account_kind(payer_account).ok_or_else(|| {
error_counters.invalid_account_for_fee += 1; error_counters.invalid_account_for_fee += 1;
TransactionError::InvalidAccountForFee TransactionError::InvalidAccountForFee
})? { })? {
@ -355,27 +354,30 @@ impl Accounts {
if payer_account.lamports() < fee + min_balance { if payer_account.lamports() < fee + min_balance {
error_counters.insufficient_funds += 1; error_counters.insufficient_funds += 1;
Err(TransactionError::InsufficientFundsForFee) return Err(TransactionError::InsufficientFundsForFee);
} else { }
payer_account payer_account
.checked_sub_lamports(fee) .checked_sub_lamports(fee)
.map_err(|_| TransactionError::InsufficientFundsForFee)?; .map_err(|_| TransactionError::InsufficientFundsForFee)?;
let message = tx.message(); let program_indices = message
let loaders = message .instructions()
.program_instructions_iter() .iter()
.map(|(program_id, _ix)| { .map(|instruction| {
self.load_executable_accounts(ancestors, program_id, error_counters) self.load_executable_accounts(
ancestors,
&mut accounts,
instruction.program_id_index as usize,
error_counters,
)
}) })
.collect::<Result<TransactionLoaders>>()?; .collect::<Result<Vec<Vec<usize>>>>()?;
Ok(LoadedTransaction { Ok(LoadedTransaction {
accounts, accounts,
loaders, program_indices,
rent: tx_rent, rent: tx_rent,
rent_debits, rent_debits,
}) })
}
}
} else { } else {
error_counters.account_not_found += 1; error_counters.account_not_found += 1;
Err(TransactionError::AccountNotFound) Err(TransactionError::AccountNotFound)
@ -386,35 +388,35 @@ impl Accounts {
fn load_executable_accounts( fn load_executable_accounts(
&self, &self,
ancestors: &Ancestors, ancestors: &Ancestors,
program_id: &Pubkey, accounts: &mut Vec<(Pubkey, AccountSharedData)>,
mut program_account_index: usize,
error_counters: &mut ErrorCounters, error_counters: &mut ErrorCounters,
) -> Result<Vec<(Pubkey, AccountSharedData)>> { ) -> Result<Vec<usize>> {
let mut accounts = Vec::new(); let mut account_indices = Vec::new();
let mut program_id = accounts[program_account_index].0;
let mut depth = 0; let mut depth = 0;
let mut program_id = *program_id; while !native_loader::check_id(&program_id) {
loop {
if native_loader::check_id(&program_id) {
// At the root of the chain, ready to dispatch
break;
}
if depth >= 5 { if depth >= 5 {
error_counters.call_chain_too_deep += 1; error_counters.call_chain_too_deep += 1;
return Err(TransactionError::CallChainTooDeep); return Err(TransactionError::CallChainTooDeep);
} }
depth += 1; depth += 1;
let program = match self program_account_index = match self
.accounts_db .accounts_db
.load_with_fixed_root(ancestors, &program_id) .load_with_fixed_root(ancestors, &program_id)
.map(|(account, _)| account)
{ {
Some(program) => program, Some((program_account, _)) => {
let account_index = accounts.len();
accounts.push((program_id, program_account));
account_index
}
None => { None => {
error_counters.account_not_found += 1; error_counters.account_not_found += 1;
return Err(TransactionError::ProgramAccountNotFound); return Err(TransactionError::ProgramAccountNotFound);
} }
}; };
let program = &accounts[program_account_index].1;
if !program.executable() { if !program.executable() {
error_counters.invalid_program_for_execution += 1; error_counters.invalid_program_for_execution += 1;
return Err(TransactionError::InvalidProgramForExecution); return Err(TransactionError::InvalidProgramForExecution);
@ -429,26 +431,31 @@ impl Accounts {
programdata_address, programdata_address,
}) = program.state() }) = program.state()
{ {
if let Some(program) = self let programdata_account_index = match self
.accounts_db .accounts_db
.load_with_fixed_root(ancestors, &programdata_address) .load_with_fixed_root(ancestors, &programdata_address)
.map(|(account, _)| account)
{ {
accounts.insert(0, (programdata_address, program)); Some((programdata_account, _)) => {
} else { let account_index = accounts.len();
accounts.push((programdata_address, programdata_account));
account_index
}
None => {
error_counters.account_not_found += 1; error_counters.account_not_found += 1;
return Err(TransactionError::ProgramAccountNotFound); return Err(TransactionError::ProgramAccountNotFound);
} }
};
account_indices.insert(0, programdata_account_index);
} else { } else {
error_counters.invalid_program_for_execution += 1; error_counters.invalid_program_for_execution += 1;
return Err(TransactionError::InvalidProgramForExecution); return Err(TransactionError::InvalidProgramForExecution);
} }
} }
accounts.insert(0, (program_id, program)); account_indices.insert(0, program_account_index);
program_id = program_owner; program_id = program_owner;
} }
Ok(accounts) Ok(account_indices)
} }
pub fn load_accounts( pub fn load_accounts(
@ -1468,8 +1475,8 @@ mod tests {
(Ok(loaded_transaction), _nonce_rollback) => { (Ok(loaded_transaction), _nonce_rollback) => {
assert_eq!(loaded_transaction.accounts.len(), 3); assert_eq!(loaded_transaction.accounts.len(), 3);
assert_eq!(loaded_transaction.accounts[0].1, accounts[0].1); assert_eq!(loaded_transaction.accounts[0].1, accounts[0].1);
assert_eq!(loaded_transaction.loaders.len(), 1); assert_eq!(loaded_transaction.program_indices.len(), 1);
assert_eq!(loaded_transaction.loaders[0].len(), 0); assert_eq!(loaded_transaction.program_indices[0].len(), 0);
} }
(Err(e), _nonce_rollback) => Err(e).unwrap(), (Err(e), _nonce_rollback) => Err(e).unwrap(),
} }
@ -1654,15 +1661,22 @@ mod tests {
assert_eq!(loaded_accounts.len(), 1); assert_eq!(loaded_accounts.len(), 1);
match &loaded_accounts[0] { match &loaded_accounts[0] {
(Ok(loaded_transaction), _nonce_rollback) => { (Ok(loaded_transaction), _nonce_rollback) => {
assert_eq!(loaded_transaction.accounts.len(), 3); assert_eq!(loaded_transaction.accounts.len(), 6);
assert_eq!(loaded_transaction.accounts[0].1, accounts[0].1); assert_eq!(loaded_transaction.accounts[0].1, accounts[0].1);
assert_eq!(loaded_transaction.loaders.len(), 2); assert_eq!(loaded_transaction.program_indices.len(), 2);
assert_eq!(loaded_transaction.loaders[0].len(), 1); assert_eq!(loaded_transaction.program_indices[0].len(), 1);
assert_eq!(loaded_transaction.loaders[1].len(), 2); assert_eq!(loaded_transaction.program_indices[1].len(), 2);
for loaders in loaded_transaction.loaders.iter() { for program_indices in loaded_transaction.program_indices.iter() {
for (i, accounts_subset) in loaders.iter().enumerate() { for (i, program_index) in program_indices.iter().enumerate() {
// +1 to skip first not loader account // +1 to skip first not loader account
assert_eq!(*accounts_subset, accounts[i + 1]); assert_eq!(
loaded_transaction.accounts[*program_index].0,
accounts[i + 1].0
);
assert_eq!(
loaded_transaction.accounts[*program_index].1,
accounts[i + 1].1
);
} }
} }
} }
@ -1752,7 +1766,7 @@ mod tests {
assert_eq!(loaded_accounts.len(), 1); assert_eq!(loaded_accounts.len(), 1);
let result = loaded_accounts[0].0.as_ref().unwrap(); let result = loaded_accounts[0].0.as_ref().unwrap();
assert_eq!(result.accounts[..2], accounts[..2]); assert_eq!(result.accounts[..2], accounts[..2]);
assert_eq!(result.loaders[0], vec![accounts[2].clone()]); assert_eq!(result.accounts[result.program_indices[0][0]], accounts[2]);
} }
#[test] #[test]
@ -1835,7 +1849,7 @@ mod tests {
assert_eq!(loaded_accounts.len(), 1); assert_eq!(loaded_accounts.len(), 1);
let result = loaded_accounts[0].0.as_ref().unwrap(); let result = loaded_accounts[0].0.as_ref().unwrap();
assert_eq!(result.accounts[..2], accounts[..2]); assert_eq!(result.accounts[..2], accounts[..2]);
assert_eq!(result.loaders[0], vec![accounts[5].clone()]); assert_eq!(result.accounts[result.program_indices[0][0]], accounts[5]);
// Solution 2: mark programdata as readonly // Solution 2: mark programdata as readonly
message.account_keys = vec![key0, key1, key2]; // revert key change message.account_keys = vec![key0, key1, key2]; // revert key change
@ -1847,14 +1861,9 @@ mod tests {
assert_eq!(loaded_accounts.len(), 1); assert_eq!(loaded_accounts.len(), 1);
let result = loaded_accounts[0].0.as_ref().unwrap(); let result = loaded_accounts[0].0.as_ref().unwrap();
assert_eq!(result.accounts[..2], accounts[..2]); assert_eq!(result.accounts[..2], accounts[..2]);
assert_eq!( assert_eq!(result.accounts[result.program_indices[0][0]], accounts[5]);
result.loaders[0], assert_eq!(result.accounts[result.program_indices[0][1]], accounts[3]);
vec![ assert_eq!(result.accounts[result.program_indices[0][2]], accounts[4]);
accounts[5].clone(),
accounts[3].clone(),
accounts[4].clone()
]
);
} }
#[test] #[test]
@ -1923,8 +1932,8 @@ mod tests {
let result = loaded_accounts[0].0.as_ref().unwrap(); let result = loaded_accounts[0].0.as_ref().unwrap();
assert_eq!(result.accounts[..2], accounts_with_upgradeable_loader[..2]); assert_eq!(result.accounts[..2], accounts_with_upgradeable_loader[..2]);
assert_eq!( assert_eq!(
result.loaders[0], result.accounts[result.program_indices[0][0]],
vec![accounts_with_upgradeable_loader[2].clone()] accounts_with_upgradeable_loader[2]
); );
// Solution 2: mark programdata as readonly // Solution 2: mark programdata as readonly
@ -1937,7 +1946,7 @@ mod tests {
assert_eq!(loaded_accounts.len(), 1); assert_eq!(loaded_accounts.len(), 1);
let result = loaded_accounts[0].0.as_ref().unwrap(); let result = loaded_accounts[0].0.as_ref().unwrap();
assert_eq!(result.accounts[..2], accounts[..2]); assert_eq!(result.accounts[..2], accounts[..2]);
assert_eq!(result.loaders[0], vec![accounts[2].clone()]); assert_eq!(result.accounts[result.program_indices[0][0]], accounts[2]);
} }
#[test] #[test]
@ -1952,11 +1961,17 @@ mod tests {
let mut error_counters = ErrorCounters::default(); let mut error_counters = ErrorCounters::default();
let ancestors = vec![(0, 0)].into_iter().collect(); let ancestors = vec![(0, 0)].into_iter().collect();
let keypair = Keypair::new();
let mut account = AccountSharedData::new(1, 0, &Pubkey::default());
account.set_executable(true);
accounts.store_slow_uncached(0, &keypair.pubkey(), &account);
assert_eq!( assert_eq!(
accounts.load_executable_accounts( accounts.load_executable_accounts(
&ancestors, &ancestors,
&solana_sdk::pubkey::new_rand(), &mut vec![(keypair.pubkey(), account)],
&mut error_counters 0,
&mut error_counters,
), ),
Err(TransactionError::ProgramAccountNotFound) Err(TransactionError::ProgramAccountNotFound)
); );
@ -2282,27 +2297,21 @@ mod tests {
]; ];
let tx1 = new_sanitized_tx(&[&keypair1], message, Hash::default()); let tx1 = new_sanitized_tx(&[&keypair1], message, Hash::default());
let loaders = vec![(Ok(()), None), (Ok(()), None)];
let transaction_loaders0 = vec![];
let transaction_rent0 = 0;
let loaded0 = ( let loaded0 = (
Ok(LoadedTransaction { Ok(LoadedTransaction {
accounts: transaction_accounts0, accounts: transaction_accounts0,
loaders: transaction_loaders0, program_indices: vec![],
rent: transaction_rent0, rent: 0,
rent_debits: RentDebits::default(), rent_debits: RentDebits::default(),
}), }),
None, None,
); );
let transaction_loaders1 = vec![];
let transaction_rent1 = 0;
let loaded1 = ( let loaded1 = (
Ok(LoadedTransaction { Ok(LoadedTransaction {
accounts: transaction_accounts1, accounts: transaction_accounts1,
loaders: transaction_loaders1, program_indices: vec![],
rent: transaction_rent1, rent: 0,
rent_debits: RentDebits::default(), rent_debits: RentDebits::default(),
}), }),
None, None,
@ -2325,9 +2334,10 @@ mod tests {
.insert_new_readonly(&pubkey); .insert_new_readonly(&pubkey);
} }
let txs = vec![tx0, tx1]; let txs = vec![tx0, tx1];
let programs = vec![(Ok(()), None), (Ok(()), None)];
let collected_accounts = accounts.collect_accounts_to_store( let collected_accounts = accounts.collect_accounts_to_store(
&txs, &txs,
&loaders, &programs,
loaded.as_mut_slice(), loaded.as_mut_slice(),
&rent_collector, &rent_collector,
&(Hash::default(), FeeCalculator::default()), &(Hash::default(), FeeCalculator::default()),
@ -2674,24 +2684,15 @@ mod tests {
nonce_account_pre.clone(), nonce_account_pre.clone(),
Some(from_account_pre.clone()), Some(from_account_pre.clone()),
)); ));
let loaders = vec![(
Err(TransactionError::InstructionError(
1,
InstructionError::InvalidArgument,
)),
nonce_rollback.clone(),
)];
let transaction_loaders = vec![];
let transaction_rent = 0;
let loaded = ( let loaded = (
Ok(LoadedTransaction { Ok(LoadedTransaction {
accounts: transaction_accounts, accounts: transaction_accounts,
loaders: transaction_loaders, program_indices: vec![],
rent: transaction_rent, rent: 0,
rent_debits: RentDebits::default(), rent_debits: RentDebits::default(),
}), }),
nonce_rollback, nonce_rollback.clone(),
); );
let mut loaded = vec![loaded]; let mut loaded = vec![loaded];
@ -2705,9 +2706,16 @@ mod tests {
AccountShrinkThreshold::default(), AccountShrinkThreshold::default(),
); );
let txs = vec![tx]; let txs = vec![tx];
let programs = vec![(
Err(TransactionError::InstructionError(
1,
InstructionError::InvalidArgument,
)),
nonce_rollback,
)];
let collected_accounts = accounts.collect_accounts_to_store( let collected_accounts = accounts.collect_accounts_to_store(
&txs, &txs,
&loaders, &programs,
loaded.as_mut_slice(), loaded.as_mut_slice(),
&rent_collector, &rent_collector,
&(next_blockhash, FeeCalculator::default()), &(next_blockhash, FeeCalculator::default()),
@ -2792,24 +2800,15 @@ mod tests {
nonce_account_pre.clone(), nonce_account_pre.clone(),
None, None,
)); ));
let loaders = vec![(
Err(TransactionError::InstructionError(
1,
InstructionError::InvalidArgument,
)),
nonce_rollback.clone(),
)];
let transaction_loaders = vec![];
let transaction_rent = 0;
let loaded = ( let loaded = (
Ok(LoadedTransaction { Ok(LoadedTransaction {
accounts: transaction_accounts, accounts: transaction_accounts,
loaders: transaction_loaders, program_indices: vec![],
rent: transaction_rent, rent: 0,
rent_debits: RentDebits::default(), rent_debits: RentDebits::default(),
}), }),
nonce_rollback, nonce_rollback.clone(),
); );
let mut loaded = vec![loaded]; let mut loaded = vec![loaded];
@ -2823,9 +2822,16 @@ mod tests {
AccountShrinkThreshold::default(), AccountShrinkThreshold::default(),
); );
let txs = vec![tx]; let txs = vec![tx];
let programs = vec![(
Err(TransactionError::InstructionError(
1,
InstructionError::InvalidArgument,
)),
nonce_rollback,
)];
let collected_accounts = accounts.collect_accounts_to_store( let collected_accounts = accounts.collect_accounts_to_store(
&txs, &txs,
&loaders, &programs,
loaded.as_mut_slice(), loaded.as_mut_slice(),
&rent_collector, &rent_collector,
&(next_blockhash, FeeCalculator::default()), &(next_blockhash, FeeCalculator::default()),

View File

@ -34,10 +34,7 @@
//! on behalf of the caller, and a low-level API for when they have //! on behalf of the caller, and a low-level API for when they have
//! already been signed and verified. //! already been signed and verified.
use crate::{ use crate::{
accounts::{ accounts::{AccountAddressFilter, Accounts, TransactionAccounts, TransactionLoadResult},
AccountAddressFilter, Accounts, TransactionAccounts, TransactionLoadResult,
TransactionLoaders,
},
accounts_db::{ accounts_db::{
AccountShrinkThreshold, AccountsDbConfig, ErrorCounters, SnapshotStorages, AccountShrinkThreshold, AccountsDbConfig, ErrorCounters, SnapshotStorages,
ACCOUNTS_DB_CONFIG_FOR_BENCHMARKS, ACCOUNTS_DB_CONFIG_FOR_TESTING, ACCOUNTS_DB_CONFIG_FOR_BENCHMARKS, ACCOUNTS_DB_CONFIG_FOR_TESTING,
@ -192,7 +189,6 @@ type BankStatusCache = StatusCache<Result<()>>;
#[frozen_abi(digest = "5Br3PNyyX1L7XoS4jYLt5JTeMXowLSsu7v9LhokC8vnq")] #[frozen_abi(digest = "5Br3PNyyX1L7XoS4jYLt5JTeMXowLSsu7v9LhokC8vnq")]
pub type BankSlotDelta = SlotDelta<Result<()>>; pub type BankSlotDelta = SlotDelta<Result<()>>;
type TransactionAccountRefCells = Vec<(Pubkey, Rc<RefCell<AccountSharedData>>)>; type TransactionAccountRefCells = Vec<(Pubkey, Rc<RefCell<AccountSharedData>>)>;
type TransactionLoaderRefCells = Vec<Vec<(Pubkey, Rc<RefCell<AccountSharedData>>)>>;
// Eager rent collection repeats in cyclic manner. // Eager rent collection repeats in cyclic manner.
// Each cycle is composed of <partition_count> number of tiny pubkey subranges // Each cycle is composed of <partition_count> number of tiny pubkey subranges
@ -2818,6 +2814,7 @@ impl Bank {
) -> TransactionSimulationResult { ) -> TransactionSimulationResult {
assert!(self.is_frozen(), "simulation bank must be frozen"); assert!(self.is_frozen(), "simulation bank must be frozen");
let number_of_accounts = transaction.message().account_keys_len();
let batch = self.prepare_simulation_batch(transaction); let batch = self.prepare_simulation_batch(transaction);
let mut timings = ExecuteTimings::default(); let mut timings = ExecuteTimings::default();
@ -2848,7 +2845,13 @@ impl Bank {
.unwrap() .unwrap()
.0 .0
.ok() .ok()
.map(|loaded_transaction| loaded_transaction.accounts.into_iter().collect::<Vec<_>>()) .map(|loaded_transaction| {
loaded_transaction
.accounts
.into_iter()
.take(number_of_accounts)
.collect::<Vec<_>>()
})
.unwrap_or_default(); .unwrap_or_default();
let units_consumed = timings let units_consumed = timings
@ -3129,32 +3132,19 @@ impl Bank {
/// Converts Accounts into RefCell<AccountSharedData>, this involves moving /// Converts Accounts into RefCell<AccountSharedData>, this involves moving
/// ownership by draining the source /// ownership by draining the source
fn accounts_to_refcells( fn accounts_to_refcells(accounts: &mut TransactionAccounts) -> TransactionAccountRefCells {
accounts: &mut TransactionAccounts,
loaders: &mut TransactionLoaders,
) -> (TransactionAccountRefCells, TransactionLoaderRefCells) {
let account_refcells: Vec<_> = accounts let account_refcells: Vec<_> = accounts
.drain(..) .drain(..)
.map(|(pubkey, account)| (pubkey, Rc::new(RefCell::new(account)))) .map(|(pubkey, account)| (pubkey, Rc::new(RefCell::new(account))))
.collect(); .collect();
let loader_refcells: Vec<Vec<_>> = loaders account_refcells
.iter_mut()
.map(|v| {
v.drain(..)
.map(|(pubkey, account)| (pubkey, Rc::new(RefCell::new(account))))
.collect()
})
.collect();
(account_refcells, loader_refcells)
} }
/// Converts back from RefCell<AccountSharedData> to AccountSharedData, this involves moving /// Converts back from RefCell<AccountSharedData> to AccountSharedData, this involves moving
/// ownership by draining the sources /// ownership by draining the sources
fn refcells_to_accounts( fn refcells_to_accounts(
accounts: &mut TransactionAccounts, accounts: &mut TransactionAccounts,
loaders: &mut TransactionLoaders,
mut account_refcells: TransactionAccountRefCells, mut account_refcells: TransactionAccountRefCells,
loader_refcells: TransactionLoaderRefCells,
) -> std::result::Result<(), TransactionError> { ) -> std::result::Result<(), TransactionError> {
for (pubkey, account_refcell) in account_refcells.drain(..) { for (pubkey, account_refcell) in account_refcells.drain(..) {
accounts.push(( accounts.push((
@ -3164,16 +3154,6 @@ impl Bank {
.into_inner(), .into_inner(),
)) ))
} }
for (ls, mut lrcs) in loaders.iter_mut().zip(loader_refcells) {
for (pubkey, lrc) in lrcs.drain(..) {
ls.push((
pubkey,
Rc::try_unwrap(lrc)
.map_err(|_| TransactionError::AccountBorrowOutstanding)?
.into_inner(),
))
}
}
Ok(()) Ok(())
} }
@ -3200,11 +3180,12 @@ impl Bank {
fn get_executors( fn get_executors(
&self, &self,
message: &SanitizedMessage, message: &SanitizedMessage,
loaders: &[Vec<(Pubkey, AccountSharedData)>], accounts: &[(Pubkey, AccountSharedData)],
program_indices: &[Vec<usize>],
) -> Rc<RefCell<Executors>> { ) -> Rc<RefCell<Executors>> {
let mut num_executors = message.account_keys_len(); let mut num_executors = message.account_keys_len();
for instruction_loaders in loaders.iter() { for program_indices_of_instruction in program_indices.iter() {
num_executors += instruction_loaders.len(); num_executors += program_indices_of_instruction.len();
} }
let mut executors = HashMap::with_capacity(num_executors); let mut executors = HashMap::with_capacity(num_executors);
let cow_cache = self.cached_executors.read().unwrap(); let cow_cache = self.cached_executors.read().unwrap();
@ -3215,10 +3196,11 @@ impl Bank {
executors.insert(*key, executor); executors.insert(*key, executor);
} }
} }
for instruction_loaders in loaders.iter() { for program_indices_of_instruction in program_indices.iter() {
for (key, _) in instruction_loaders.iter() { for account_index in program_indices_of_instruction.iter() {
if let Some(executor) = cache.get(key) { let key = accounts[*account_index].0;
executors.insert(*key, executor); if let Some(executor) = cache.get(&key) {
executors.insert(key, executor);
} }
} }
} }
@ -3334,14 +3316,15 @@ impl Bank {
}; };
if process_result.is_ok() { if process_result.is_ok() {
let executors = let executors = self.get_executors(
self.get_executors(tx.message(), &loaded_transaction.loaders); tx.message(),
&loaded_transaction.accounts,
let (account_refcells, loader_refcells) = Self::accounts_to_refcells( &loaded_transaction.program_indices,
&mut loaded_transaction.accounts,
&mut loaded_transaction.loaders,
); );
let account_refcells =
Self::accounts_to_refcells(&mut loaded_transaction.accounts);
let instruction_recorders = if enable_cpi_recording { let instruction_recorders = if enable_cpi_recording {
let ix_count = tx.message().instructions().len(); let ix_count = tx.message().instructions().len();
let mut recorders = Vec::with_capacity(ix_count); let mut recorders = Vec::with_capacity(ix_count);
@ -3377,7 +3360,7 @@ impl Bank {
if let Some(legacy_message) = tx.message().legacy_message() { if let Some(legacy_message) = tx.message().legacy_message() {
process_result = self.message_processor.process_message( process_result = self.message_processor.process_message(
legacy_message, legacy_message,
&loader_refcells, &loaded_transaction.program_indices,
&account_refcells, &account_refcells,
&self.rent_collector, &self.rent_collector,
log_collector.clone(), log_collector.clone(),
@ -3405,9 +3388,7 @@ impl Bank {
if let Err(e) = Self::refcells_to_accounts( if let Err(e) = Self::refcells_to_accounts(
&mut loaded_transaction.accounts, &mut loaded_transaction.accounts,
&mut loaded_transaction.loaders,
account_refcells, account_refcells,
loader_refcells,
) { ) {
warn!("Account lifetime mismanagement"); warn!("Account lifetime mismanagement");
process_result = Err(e); process_result = Err(e);
@ -12013,12 +11994,11 @@ pub(crate) mod tests {
.try_into() .try_into()
.unwrap(); .unwrap();
let loaders = &[ let program_indices = &[vec![0, 1], vec![2]];
vec![ let accounts = &[
(key3, AccountSharedData::default()), (key3, AccountSharedData::default()),
(key4, AccountSharedData::default()), (key4, AccountSharedData::default()),
], (key1, AccountSharedData::default()),
vec![(key1, AccountSharedData::default())],
]; ];
// don't do any work if not dirty // don't do any work if not dirty
@ -12030,7 +12010,7 @@ pub(crate) mod tests {
let executors = Rc::new(RefCell::new(executors)); let executors = Rc::new(RefCell::new(executors));
executors.borrow_mut().is_dirty = false; executors.borrow_mut().is_dirty = false;
bank.update_executors(executors); bank.update_executors(executors);
let executors = bank.get_executors(&message, loaders); let executors = bank.get_executors(&message, accounts, program_indices);
assert_eq!(executors.borrow().executors.len(), 0); assert_eq!(executors.borrow().executors.len(), 0);
// do work // do work
@ -12041,7 +12021,7 @@ pub(crate) mod tests {
executors.insert(key4, executor.clone()); executors.insert(key4, executor.clone());
let executors = Rc::new(RefCell::new(executors)); let executors = Rc::new(RefCell::new(executors));
bank.update_executors(executors); bank.update_executors(executors);
let executors = bank.get_executors(&message, loaders); let executors = bank.get_executors(&message, accounts, program_indices);
assert_eq!(executors.borrow().executors.len(), 4); assert_eq!(executors.borrow().executors.len(), 4);
assert!(executors.borrow().executors.contains_key(&key1)); assert!(executors.borrow().executors.contains_key(&key1));
assert!(executors.borrow().executors.contains_key(&key2)); assert!(executors.borrow().executors.contains_key(&key2));
@ -12050,7 +12030,7 @@ pub(crate) mod tests {
// Check inheritance // Check inheritance
let bank = Bank::new_from_parent(&Arc::new(bank), &solana_sdk::pubkey::new_rand(), 1); let bank = Bank::new_from_parent(&Arc::new(bank), &solana_sdk::pubkey::new_rand(), 1);
let executors = bank.get_executors(&message, loaders); let executors = bank.get_executors(&message, accounts, program_indices);
assert_eq!(executors.borrow().executors.len(), 4); assert_eq!(executors.borrow().executors.len(), 4);
assert!(executors.borrow().executors.contains_key(&key1)); assert!(executors.borrow().executors.contains_key(&key1));
assert!(executors.borrow().executors.contains_key(&key2)); assert!(executors.borrow().executors.contains_key(&key2));
@ -12061,7 +12041,7 @@ pub(crate) mod tests {
bank.remove_executor(&key2); bank.remove_executor(&key2);
bank.remove_executor(&key3); bank.remove_executor(&key3);
bank.remove_executor(&key4); bank.remove_executor(&key4);
let executors = bank.get_executors(&message, loaders); let executors = bank.get_executors(&message, accounts, program_indices);
assert_eq!(executors.borrow().executors.len(), 0); assert_eq!(executors.borrow().executors.len(), 0);
assert!(!executors.borrow().executors.contains_key(&key1)); assert!(!executors.borrow().executors.contains_key(&key1));
assert!(!executors.borrow().executors.contains_key(&key2)); assert!(!executors.borrow().executors.contains_key(&key2));
@ -12082,25 +12062,26 @@ pub(crate) mod tests {
let message = let message =
SanitizedMessage::try_from(Message::new(&[], Some(&Pubkey::new_unique()))).unwrap(); SanitizedMessage::try_from(Message::new(&[], Some(&Pubkey::new_unique()))).unwrap();
let loaders = &[vec![ let program_indices = &[vec![0, 1]];
let accounts = &[
(key1, AccountSharedData::default()), (key1, AccountSharedData::default()),
(key2, AccountSharedData::default()), (key2, AccountSharedData::default()),
]]; ];
// add one to root bank // add one to root bank
let mut executors = Executors::default(); let mut executors = Executors::default();
executors.insert(key1, executor.clone()); executors.insert(key1, executor.clone());
let executors = Rc::new(RefCell::new(executors)); let executors = Rc::new(RefCell::new(executors));
root.update_executors(executors); root.update_executors(executors);
let executors = root.get_executors(&message, loaders); let executors = root.get_executors(&message, accounts, program_indices);
assert_eq!(executors.borrow().executors.len(), 1); assert_eq!(executors.borrow().executors.len(), 1);
let fork1 = Bank::new_from_parent(&root, &Pubkey::default(), 1); let fork1 = Bank::new_from_parent(&root, &Pubkey::default(), 1);
let fork2 = Bank::new_from_parent(&root, &Pubkey::default(), 1); let fork2 = Bank::new_from_parent(&root, &Pubkey::default(), 1);
let executors = fork1.get_executors(&message, loaders); let executors = fork1.get_executors(&message, accounts, program_indices);
assert_eq!(executors.borrow().executors.len(), 1); assert_eq!(executors.borrow().executors.len(), 1);
let executors = fork2.get_executors(&message, loaders); let executors = fork2.get_executors(&message, accounts, program_indices);
assert_eq!(executors.borrow().executors.len(), 1); assert_eq!(executors.borrow().executors.len(), 1);
let mut executors = Executors::default(); let mut executors = Executors::default();
@ -12108,16 +12089,16 @@ pub(crate) mod tests {
let executors = Rc::new(RefCell::new(executors)); let executors = Rc::new(RefCell::new(executors));
fork1.update_executors(executors); fork1.update_executors(executors);
let executors = fork1.get_executors(&message, loaders); let executors = fork1.get_executors(&message, accounts, program_indices);
assert_eq!(executors.borrow().executors.len(), 2); assert_eq!(executors.borrow().executors.len(), 2);
let executors = fork2.get_executors(&message, loaders); let executors = fork2.get_executors(&message, accounts, program_indices);
assert_eq!(executors.borrow().executors.len(), 1); assert_eq!(executors.borrow().executors.len(), 1);
fork1.remove_executor(&key1); fork1.remove_executor(&key1);
let executors = fork1.get_executors(&message, loaders); let executors = fork1.get_executors(&message, accounts, program_indices);
assert_eq!(executors.borrow().executors.len(), 1); assert_eq!(executors.borrow().executors.len(), 1);
let executors = fork2.get_executors(&message, loaders); let executors = fork2.get_executors(&message, accounts, program_indices);
assert_eq!(executors.borrow().executors.len(), 1); assert_eq!(executors.borrow().executors.len(), 1);
} }

View File

@ -75,7 +75,7 @@ impl<'a> ThisInvokeContext<'a> {
rent: Rent, rent: Rent,
message: &'a Message, message: &'a Message,
instruction: &'a CompiledInstruction, instruction: &'a CompiledInstruction,
executable_accounts: &'a [(Pubkey, Rc<RefCell<AccountSharedData>>)], program_indices: &[usize],
accounts: &'a [(Pubkey, Rc<RefCell<AccountSharedData>>)], accounts: &'a [(Pubkey, Rc<RefCell<AccountSharedData>>)],
programs: &'a [(Pubkey, ProcessInstructionWithContext)], programs: &'a [(Pubkey, ProcessInstructionWithContext)],
log_collector: Option<Rc<LogCollector>>, log_collector: Option<Rc<LogCollector>>,
@ -88,15 +88,8 @@ impl<'a> ThisInvokeContext<'a> {
ancestors: &'a Ancestors, ancestors: &'a Ancestors,
blockhash: &'a Hash, blockhash: &'a Hash,
fee_calculator: &'a FeeCalculator, fee_calculator: &'a FeeCalculator,
) -> Self { ) -> Result<Self, InstructionError> {
let pre_accounts = MessageProcessor::create_pre_accounts(message, instruction, accounts); let pre_accounts = MessageProcessor::create_pre_accounts(message, instruction, accounts);
let keyed_accounts = InstructionProcessor::create_keyed_accounts(
message,
instruction,
executable_accounts,
accounts,
feature_set.is_active(&demote_program_write_locks::id()),
);
let compute_meter = if feature_set.is_active(&tx_wide_compute_cap::id()) { let compute_meter = if feature_set.is_active(&tx_wide_compute_cap::id()) {
compute_meter compute_meter
} else { } else {
@ -124,20 +117,18 @@ impl<'a> ThisInvokeContext<'a> {
blockhash, blockhash,
fee_calculator, fee_calculator,
}; };
invoke_context invoke_context.push(program_id, message, instruction, program_indices, accounts)?;
.invoke_stack Ok(invoke_context)
.push(InvokeContextStackFrame::new(
*program_id,
create_keyed_accounts_unified(&keyed_accounts),
));
invoke_context
} }
} }
impl<'a> InvokeContext for ThisInvokeContext<'a> { impl<'a> InvokeContext for ThisInvokeContext<'a> {
fn push( fn push(
&mut self, &mut self,
key: &Pubkey, key: &Pubkey,
keyed_accounts: &[(bool, bool, &Pubkey, &RefCell<AccountSharedData>)], message: &Message,
instruction: &CompiledInstruction,
program_indices: &[usize],
instruction_accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)],
) -> 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);
@ -154,6 +145,31 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> {
return Err(InstructionError::ReentrancyNotAllowed); return Err(InstructionError::ReentrancyNotAllowed);
} }
// Create the KeyedAccounts that will be passed to the program
let demote_program_write_locks = self
.feature_set
.is_active(&demote_program_write_locks::id());
let keyed_accounts = program_indices
.iter()
.map(|account_index| {
(
false,
false,
&self.accounts[*account_index].0,
&self.accounts[*account_index].1 as &RefCell<AccountSharedData>,
)
})
.chain(instruction.accounts.iter().map(|index| {
let index = *index as usize;
(
message.is_signer(index),
message.is_writable(index, demote_program_write_locks),
&instruction_accounts[index].0,
&instruction_accounts[index].1 as &RefCell<AccountSharedData>,
)
}))
.collect::<Vec<_>>();
// Alias the keys and account references in the provided keyed_accounts // Alias the keys and account references in the provided keyed_accounts
// with the ones already existing in self, so that the lifetime 'a matches. // 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 { fn transmute_lifetime<'a, 'b, T: Sized>(value: &'a T) -> &'b T {
@ -264,14 +280,13 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> {
fn is_feature_active(&self, feature_id: &Pubkey) -> bool { fn is_feature_active(&self, feature_id: &Pubkey) -> bool {
self.feature_set.is_active(feature_id) self.feature_set.is_active(feature_id)
} }
fn get_account(&self, pubkey: &Pubkey) -> Option<Rc<RefCell<AccountSharedData>>> { fn get_account(&self, pubkey: &Pubkey) -> Option<(usize, Rc<RefCell<AccountSharedData>>)> {
self.accounts.iter().find_map(|(key, account)| { for (index, (key, account)) in self.accounts.iter().enumerate().rev() {
if key == pubkey { if key == pubkey {
Some(account.clone()) return Some((index, account.clone()));
} else {
None
} }
}) }
None
} }
fn update_timing( fn update_timing(
&mut self, &mut self,
@ -381,9 +396,11 @@ impl MessageProcessor {
/// Verify there are no outstanding borrows /// Verify there are no outstanding borrows
pub fn verify_account_references( pub fn verify_account_references(
accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)], accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)],
program_indices: &[usize],
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
for (_, account) in accounts.iter() { for account_index in program_indices.iter() {
account accounts[*account_index]
.1
.try_borrow_mut() .try_borrow_mut()
.map_err(|_| InstructionError::AccountBorrowOutstanding)?; .map_err(|_| InstructionError::AccountBorrowOutstanding)?;
} }
@ -396,7 +413,7 @@ impl MessageProcessor {
message: &Message, message: &Message,
instruction: &CompiledInstruction, instruction: &CompiledInstruction,
pre_accounts: &[PreAccount], pre_accounts: &[PreAccount],
executable_accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)], program_indices: &[usize],
accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)], accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)],
rent: &Rent, rent: &Rent,
timings: &mut ExecuteDetailsTimings, timings: &mut ExecuteDetailsTimings,
@ -405,7 +422,7 @@ impl MessageProcessor {
demote_program_write_locks: bool, demote_program_write_locks: bool,
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
// Verify all executable accounts have zero outstanding refs // Verify all executable accounts have zero outstanding refs
Self::verify_account_references(executable_accounts)?; Self::verify_account_references(accounts, program_indices)?;
// Verify the per-account instruction results // Verify the per-account instruction results
let (mut pre_sum, mut post_sum) = (0_u128, 0_u128); let (mut pre_sum, mut post_sum) = (0_u128, 0_u128);
@ -462,7 +479,7 @@ impl MessageProcessor {
&self, &self,
message: &Message, message: &Message,
instruction: &CompiledInstruction, instruction: &CompiledInstruction,
executable_accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)], program_indices: &[usize],
accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)], accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)],
rent_collector: &RentCollector, rent_collector: &RentCollector,
log_collector: Option<Rc<LogCollector>>, log_collector: Option<Rc<LogCollector>>,
@ -508,7 +525,7 @@ impl MessageProcessor {
rent_collector.rent, rent_collector.rent,
message, message,
instruction, instruction,
executable_accounts, program_indices,
accounts, accounts,
programs, programs,
log_collector, log_collector,
@ -521,7 +538,7 @@ impl MessageProcessor {
ancestors, ancestors,
blockhash, blockhash,
fee_calculator, fee_calculator,
); )?;
self.instruction_processor.process_instruction( self.instruction_processor.process_instruction(
program_id, program_id,
@ -532,7 +549,7 @@ impl MessageProcessor {
message, message,
instruction, instruction,
&invoke_context.pre_accounts, &invoke_context.pre_accounts,
executable_accounts, program_indices,
accounts, accounts,
&rent_collector.rent, &rent_collector.rent,
timings, timings,
@ -554,7 +571,7 @@ impl MessageProcessor {
pub fn process_message( pub fn process_message(
&self, &self,
message: &Message, message: &Message,
loaders: &[Vec<(Pubkey, Rc<RefCell<AccountSharedData>>)>], program_indices: &[Vec<usize>],
accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)], accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)],
rent_collector: &RentCollector, rent_collector: &RentCollector,
log_collector: Option<Rc<LogCollector>>, log_collector: Option<Rc<LogCollector>>,
@ -579,7 +596,7 @@ impl MessageProcessor {
.execute_instruction( .execute_instruction(
message, message,
instruction, instruction,
&loaders[instruction_index], &program_indices[instruction_index],
accounts, accounts,
rent_collector, rent_collector,
log_collector.clone(), log_collector.clone(),
@ -676,12 +693,21 @@ mod tests {
&ancestors, &ancestors,
&blockhash, &blockhash,
&fee_calculator, &fee_calculator,
); )
.unwrap();
// Check call depth increases and has a limit // Check call depth increases and has a limit
let mut depth_reached = 1; let mut depth_reached = 1;
for program_id in invoke_stack.iter().skip(1) { for program_id in invoke_stack.iter().skip(1) {
if Err(InstructionError::CallDepth) == invoke_context.push(program_id, &[]) { if Err(InstructionError::CallDepth)
== invoke_context.push(
program_id,
&message,
&message.instructions[0],
&[],
&accounts,
)
{
break; break;
} }
depth_reached += 1; depth_reached += 1;
@ -754,11 +780,11 @@ mod tests {
Rc::new(RefCell::new(AccountSharedData::default())), Rc::new(RefCell::new(AccountSharedData::default())),
)]; )];
assert!(MessageProcessor::verify_account_references(&accounts).is_ok()); assert!(MessageProcessor::verify_account_references(&accounts, &[0]).is_ok());
let mut _borrowed = accounts[0].1.borrow(); let mut _borrowed = accounts[0].1.borrow();
assert_eq!( assert_eq!(
MessageProcessor::verify_account_references(&accounts), MessageProcessor::verify_account_references(&accounts, &[0]),
Err(InstructionError::AccountBorrowOutstanding) Err(InstructionError::AccountBorrowOutstanding)
); );
} }
@ -808,6 +834,9 @@ mod tests {
let mut message_processor = MessageProcessor::default(); let mut message_processor = MessageProcessor::default();
message_processor.add_program(mock_system_program_id, mock_system_process_instruction); message_processor.add_program(mock_system_program_id, mock_system_process_instruction);
let program_account = Rc::new(RefCell::new(create_loadable_account_for_test(
"mock_system_program",
)));
let accounts = vec![ let accounts = vec![
( (
solana_sdk::pubkey::new_rand(), solana_sdk::pubkey::new_rand(),
@ -817,12 +846,9 @@ mod tests {
solana_sdk::pubkey::new_rand(), solana_sdk::pubkey::new_rand(),
AccountSharedData::new_ref(0, 1, &mock_system_program_id), AccountSharedData::new_ref(0, 1, &mock_system_program_id),
), ),
(mock_system_program_id, program_account),
]; ];
let program_indices = vec![vec![2]];
let account = Rc::new(RefCell::new(create_loadable_account_for_test(
"mock_system_program",
)));
let loaders = vec![vec![(mock_system_program_id, account)]];
let executors = Rc::new(RefCell::new(Executors::default())); let executors = Rc::new(RefCell::new(Executors::default()));
let ancestors = Ancestors::default(); let ancestors = Ancestors::default();
@ -842,7 +868,7 @@ mod tests {
let result = message_processor.process_message( let result = message_processor.process_message(
&message, &message,
&loaders, &program_indices,
&accounts, &accounts,
&rent_collector, &rent_collector,
None, None,
@ -872,7 +898,7 @@ mod tests {
let result = message_processor.process_message( let result = message_processor.process_message(
&message, &message,
&loaders, &program_indices,
&accounts, &accounts,
&rent_collector, &rent_collector,
None, None,
@ -906,7 +932,7 @@ mod tests {
let result = message_processor.process_message( let result = message_processor.process_message(
&message, &message,
&loaders, &program_indices,
&accounts, &accounts,
&rent_collector, &rent_collector,
None, None,
@ -996,6 +1022,9 @@ mod tests {
let mut message_processor = MessageProcessor::default(); let mut message_processor = MessageProcessor::default();
message_processor.add_program(mock_program_id, mock_system_process_instruction); message_processor.add_program(mock_program_id, mock_system_process_instruction);
let program_account = Rc::new(RefCell::new(create_loadable_account_for_test(
"mock_system_program",
)));
let accounts = vec![ let accounts = vec![
( (
solana_sdk::pubkey::new_rand(), solana_sdk::pubkey::new_rand(),
@ -1005,12 +1034,9 @@ mod tests {
solana_sdk::pubkey::new_rand(), solana_sdk::pubkey::new_rand(),
AccountSharedData::new_ref(0, 1, &mock_program_id), AccountSharedData::new_ref(0, 1, &mock_program_id),
), ),
(mock_program_id, program_account),
]; ];
let program_indices = vec![vec![2]];
let account = Rc::new(RefCell::new(create_loadable_account_for_test(
"mock_system_program",
)));
let loaders = vec![vec![(mock_program_id, account)]];
let executors = Rc::new(RefCell::new(Executors::default())); let executors = Rc::new(RefCell::new(Executors::default()));
let ancestors = Ancestors::default(); let ancestors = Ancestors::default();
@ -1032,7 +1058,7 @@ mod tests {
); );
let result = message_processor.process_message( let result = message_processor.process_message(
&message, &message,
&loaders, &program_indices,
&accounts, &accounts,
&rent_collector, &rent_collector,
None, None,
@ -1066,7 +1092,7 @@ mod tests {
); );
let result = message_processor.process_message( let result = message_processor.process_message(
&message, &message,
&loaders, &program_indices,
&accounts, &accounts,
&rent_collector, &rent_collector,
None, None,
@ -1098,7 +1124,7 @@ mod tests {
let ancestors = Ancestors::default(); let ancestors = Ancestors::default();
let result = message_processor.process_message( let result = message_processor.process_message(
&message, &message,
&loaders, &program_indices,
&accounts, &accounts,
&rent_collector, &rent_collector,
None, None,
@ -1163,10 +1189,6 @@ mod tests {
let mut program_account = AccountSharedData::new(1, 0, &native_loader::id()); let mut program_account = AccountSharedData::new(1, 0, &native_loader::id());
program_account.set_executable(true); program_account.set_executable(true);
let executable_accounts = vec![(
callee_program_id,
Rc::new(RefCell::new(program_account.clone())),
)];
let owned_account = AccountSharedData::new(42, 1, &callee_program_id); let owned_account = AccountSharedData::new(42, 1, &callee_program_id);
let not_owned_account = AccountSharedData::new(84, 1, &solana_sdk::pubkey::new_rand()); let not_owned_account = AccountSharedData::new(84, 1, &solana_sdk::pubkey::new_rand());
@ -1181,8 +1203,12 @@ mod tests {
solana_sdk::pubkey::new_rand(), solana_sdk::pubkey::new_rand(),
Rc::new(RefCell::new(not_owned_account)), Rc::new(RefCell::new(not_owned_account)),
), ),
(callee_program_id, Rc::new(RefCell::new(program_account))), (
callee_program_id,
Rc::new(RefCell::new(program_account.clone())),
),
]; ];
let program_indices = vec![2];
let compiled_instruction = CompiledInstruction::new(2, &(), vec![0, 1, 2]); let compiled_instruction = CompiledInstruction::new(2, &(), vec![0, 1, 2]);
let programs: Vec<(_, ProcessInstructionWithContext)> = let programs: Vec<(_, ProcessInstructionWithContext)> =
@ -1209,7 +1235,7 @@ mod tests {
Rent::default(), Rent::default(),
&message, &message,
&compiled_instruction, &compiled_instruction,
&executable_accounts, &program_indices,
&accounts, &accounts,
programs.as_slice(), programs.as_slice(),
None, None,
@ -1222,7 +1248,8 @@ mod tests {
&ancestors, &ancestors,
&blockhash, &blockhash,
&fee_calculator, &fee_calculator,
); )
.unwrap();
// not owned account modified by the caller (before the invoke) // not owned account modified by the caller (before the invoke)
let caller_write_privileges = message let caller_write_privileges = message
@ -1235,7 +1262,7 @@ mod tests {
assert_eq!( assert_eq!(
InstructionProcessor::process_cross_program_instruction( InstructionProcessor::process_cross_program_instruction(
&message, &message,
&executable_accounts, &program_indices,
&accounts, &accounts,
&caller_write_privileges, &caller_write_privileges,
&mut invoke_context, &mut invoke_context,
@ -1270,7 +1297,7 @@ mod tests {
Rent::default(), Rent::default(),
&message, &message,
&compiled_instruction, &compiled_instruction,
&executable_accounts, &program_indices,
&accounts, &accounts,
programs.as_slice(), programs.as_slice(),
None, None,
@ -1283,7 +1310,8 @@ mod tests {
&ancestors, &ancestors,
&blockhash, &blockhash,
&fee_calculator, &fee_calculator,
); )
.unwrap();
let caller_write_privileges = message let caller_write_privileges = message
.account_keys .account_keys
@ -1294,7 +1322,7 @@ mod tests {
assert_eq!( assert_eq!(
InstructionProcessor::process_cross_program_instruction( InstructionProcessor::process_cross_program_instruction(
&message, &message,
&executable_accounts, &program_indices,
&accounts, &accounts,
&caller_write_privileges, &caller_write_privileges,
&mut invoke_context, &mut invoke_context,

View File

@ -7,6 +7,7 @@ use solana_sdk::{
hash::Hash, hash::Hash,
instruction::{CompiledInstruction, Instruction, InstructionError}, instruction::{CompiledInstruction, Instruction, InstructionError},
keyed_account::{create_keyed_accounts_unified, KeyedAccount}, keyed_account::{create_keyed_accounts_unified, KeyedAccount},
message::Message,
pubkey::Pubkey, pubkey::Pubkey,
sysvar::Sysvar, sysvar::Sysvar,
}; };
@ -55,7 +56,10 @@ pub trait InvokeContext {
fn push( fn push(
&mut self, &mut self,
key: &Pubkey, key: &Pubkey,
keyed_accounts: &[(bool, bool, &Pubkey, &RefCell<AccountSharedData>)], message: &Message,
instruction: &CompiledInstruction,
program_indices: &[usize],
instruction_accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)],
) -> Result<(), InstructionError>; ) -> Result<(), InstructionError>;
/// Pop a stack frame from the invocation stack /// Pop a stack frame from the invocation stack
/// ///
@ -95,8 +99,8 @@ pub trait InvokeContext {
fn record_instruction(&self, instruction: &Instruction); fn record_instruction(&self, instruction: &Instruction);
/// Get the bank's active feature set /// Get the bank's active feature set
fn is_feature_active(&self, feature_id: &Pubkey) -> bool; fn is_feature_active(&self, feature_id: &Pubkey) -> bool;
/// Get an account by its key /// Find an account_index and account by its key
fn get_account(&self, pubkey: &Pubkey) -> Option<Rc<RefCell<AccountSharedData>>>; fn get_account(&self, pubkey: &Pubkey) -> Option<(usize, Rc<RefCell<AccountSharedData>>)>;
/// Update timing /// Update timing
fn update_timing( fn update_timing(
&mut self, &mut self,
@ -440,15 +444,15 @@ pub fn mock_set_sysvar<T: Sysvar>(
impl<'a> InvokeContext for MockInvokeContext<'a> { impl<'a> InvokeContext for MockInvokeContext<'a> {
fn push( fn push(
&mut self, &mut self,
key: &Pubkey, _key: &Pubkey,
keyed_accounts: &[(bool, bool, &Pubkey, &RefCell<AccountSharedData>)], _message: &Message,
_instruction: &CompiledInstruction,
_program_indices: &[usize],
_instruction_accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)],
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
fn transmute_lifetime<'a, 'b>(value: Vec<KeyedAccount<'a>>) -> Vec<KeyedAccount<'b>> {
unsafe { std::mem::transmute(value) }
}
self.invoke_stack.push(InvokeContextStackFrame::new( self.invoke_stack.push(InvokeContextStackFrame::new(
*key, *_key,
transmute_lifetime(create_keyed_accounts_unified(keyed_accounts)), create_keyed_accounts_unified(&[]),
)); ));
Ok(()) Ok(())
} }
@ -509,10 +513,10 @@ impl<'a> InvokeContext for MockInvokeContext<'a> {
fn is_feature_active(&self, feature_id: &Pubkey) -> bool { fn is_feature_active(&self, feature_id: &Pubkey) -> bool {
!self.disabled_features.contains(feature_id) !self.disabled_features.contains(feature_id)
} }
fn get_account(&self, pubkey: &Pubkey) -> Option<Rc<RefCell<AccountSharedData>>> { fn get_account(&self, pubkey: &Pubkey) -> Option<(usize, Rc<RefCell<AccountSharedData>>)> {
for (key, account) in self.accounts.iter() { for (index, (key, account)) in self.accounts.iter().enumerate().rev() {
if key == pubkey { if key == pubkey {
return Some(account.clone()); return Some((index, account.clone()));
} }
} }
None None