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:
committed by
GitHub
parent
4386e09710
commit
88c1b8f047
@ -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(
|
||||||
|
@ -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,
|
||||||
|
@ -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()?)),
|
||||||
|
@ -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()),
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
||||||
|
Reference in New Issue
Block a user