Refactor: Remove `KeyedAccount` from program runtime (#22226)

* Makes error handling in BorrowedAccount optional.
Adds BorrowedAccount ::get_rent_epoch().
Exposes InstructionContext::get_index_in_transaction().
Turns accounts and account_keys into pinned boxed slices.

* Introduces "unsafe" to InvokeContext::push().

* Turns &TransactionContext into &mut TransactionContext in InvokeContext.

* Push and pop InstructionContext in InvokeContext.
Makes test_process_cross_program and test_native_invoke symmetric.
Removes the borrow check from test_invoke_context_verify.

* Removes keyed_accounts from prepare_instruction()

* Removes usage of invoke_stack.

* Removes keyed_accounts from program-test.

* Removes caller_write_privileges.

* Removes keyed_accounts from BPF parameter (de-)serialization.
This commit is contained in:
Alexander Meißner 2022-01-03 23:30:56 +01:00 committed by GitHub
parent 672fed04cb
commit 73e6038986
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 847 additions and 805 deletions

View File

@ -1991,8 +1991,8 @@ fn read_and_verify_elf(program_location: &str) -> Result<Vec<u8>, Box<dyn std::e
let mut program_data = Vec::new();
file.read_to_end(&mut program_data)
.map_err(|err| format!("Unable to read program file: {}", err))?;
let transaction_context = TransactionContext::new(Vec::new(), 1);
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
// Verify the program
Executable::<BpfError, ThisInstructionMeter>::from_elf(

View File

@ -6,7 +6,6 @@ use {
},
solana_sdk::{
account::{AccountSharedData, ReadableAccount},
account_utils::StateMut,
bpf_loader_upgradeable::{self, UpgradeableLoaderState},
compute_budget::ComputeBudget,
feature_set::{
@ -15,7 +14,7 @@ use {
},
hash::Hash,
instruction::{AccountMeta, CompiledInstruction, Instruction, InstructionError},
keyed_account::{create_keyed_accounts_unified, keyed_account_at_index, KeyedAccount},
keyed_account::{create_keyed_accounts_unified, KeyedAccount},
native_loader,
pubkey::Pubkey,
rent::Rent,
@ -135,7 +134,7 @@ impl<'a> StackFrame<'a> {
}
pub struct InvokeContext<'a> {
pub transaction_context: &'a TransactionContext,
pub transaction_context: &'a mut TransactionContext,
pub return_data: (Pubkey, Vec<u8>),
invoke_stack: Vec<StackFrame<'a>>,
rent: Rent,
@ -158,7 +157,7 @@ pub struct InvokeContext<'a> {
impl<'a> InvokeContext<'a> {
#[allow(clippy::too_many_arguments)]
pub fn new(
transaction_context: &'a TransactionContext,
transaction_context: &'a mut TransactionContext,
rent: Rent,
builtin_programs: &'a [BuiltinProgram],
sysvars: &'a [(Pubkey, Vec<u8>)],
@ -194,7 +193,7 @@ impl<'a> InvokeContext<'a> {
}
pub fn new_mock(
transaction_context: &'a TransactionContext,
transaction_context: &'a mut TransactionContext,
builtin_programs: &'a [BuiltinProgram],
) -> Self {
Self::new(
@ -218,8 +217,13 @@ impl<'a> InvokeContext<'a> {
&mut self,
instruction_accounts: &[InstructionAccount],
program_indices: &[usize],
instruction_data: &[u8],
) -> Result<(), InstructionError> {
if self.invoke_stack.len() > self.compute_budget.max_invoke_depth {
if self
.transaction_context
.get_instruction_context_stack_height()
> self.compute_budget.max_invoke_depth
{
return Err(InstructionError::CallDepth);
}
@ -234,7 +238,11 @@ impl<'a> InvokeContext<'a> {
{
return Err(InstructionError::UnsupportedProgramId);
}
if self.invoke_stack.is_empty() {
if self
.transaction_context
.get_instruction_context_stack_height()
== 0
{
let mut compute_budget = self.compute_budget;
if !self.feature_set.is_active(&tx_wide_compute_cap::id())
&& self.feature_set.is_active(&neon_evm_compute_budget::id())
@ -278,15 +286,26 @@ impl<'a> InvokeContext<'a> {
};
visit_each_account_once(instruction_accounts, &mut work)?;
} else {
let contains = self
.invoke_stack
.iter()
.any(|frame| frame.program_id() == program_id);
let is_last = if let Some(last_frame) = self.invoke_stack.last() {
last_frame.program_id() == program_id
} else {
false
};
let contains = (0..self
.transaction_context
.get_instruction_context_stack_height())
.any(|level| {
self.transaction_context
.get_instruction_context_at(level)
.and_then(|instruction_context| {
instruction_context.try_borrow_program_account(self.transaction_context)
})
.map(|program_account| Some(program_account.get_key()) == program_id)
.unwrap_or_else(|_| program_id.is_none())
});
let is_last = self
.transaction_context
.get_current_instruction_context()
.and_then(|instruction_context| {
instruction_context.try_borrow_program_account(self.transaction_context)
})
.map(|program_account| Some(program_account.get_key()) == program_id)
.unwrap_or_else(|_| program_id.is_none());
if contains && !is_last {
// Reentrancy not allowed unless caller is calling itself
return Err(InstructionError::ReentrancyNotAllowed);
@ -318,21 +337,27 @@ impl<'a> InvokeContext<'a> {
}))
.collect::<Vec<_>>();
// Unsafe will be removed together with the keyed_accounts
self.invoke_stack.push(StackFrame::new(
program_indices.len(),
create_keyed_accounts_unified(keyed_accounts.as_slice()),
create_keyed_accounts_unified(unsafe {
std::mem::transmute(keyed_accounts.as_slice())
}),
));
Ok(())
self.transaction_context
.push(program_indices, instruction_accounts, instruction_data)
}
/// Pop a stack frame from the invocation stack
pub fn pop(&mut self) {
pub fn pop(&mut self) -> Result<(), InstructionError> {
self.invoke_stack.pop();
self.transaction_context.pop()
}
/// Current depth of the invocation stack
pub fn invoke_depth(&self) -> usize {
self.invoke_stack.len()
self.transaction_context
.get_instruction_context_stack_height()
}
/// Verify the results of an instruction
@ -341,12 +366,11 @@ impl<'a> InvokeContext<'a> {
instruction_accounts: &[InstructionAccount],
program_indices: &[usize],
) -> Result<(), InstructionError> {
let program_id = self
.invoke_stack
.last()
.and_then(|frame| frame.program_id())
.ok_or(InstructionError::CallDepth)?;
let do_support_realloc = self.feature_set.is_active(&do_support_realloc::id());
let program_id = self
.transaction_context
.get_program_key()
.map_err(|_| InstructionError::CallDepth)?;
// Verify all executable accounts have zero outstanding refs
for account_index in program_indices.iter() {
@ -414,23 +438,18 @@ impl<'a> InvokeContext<'a> {
fn verify_and_update(
&mut self,
instruction_accounts: &[InstructionAccount],
caller_write_privileges: Option<&[bool]>,
before_instruction_context_push: bool,
) -> Result<(), InstructionError> {
let do_support_realloc = self.feature_set.is_active(&do_support_realloc::id());
let program_id = self
.invoke_stack
.last()
.and_then(|frame| frame.program_id())
.ok_or(InstructionError::CallDepth)?;
let rent = &self.rent;
let log_collector = &self.log_collector;
let transaction_context = self.transaction_context;
let pre_accounts = &mut self.pre_accounts;
let timings = &mut self.timings;
let transaction_context = &self.transaction_context;
let instruction_context = transaction_context.get_current_instruction_context()?;
let program_id = transaction_context
.get_program_key()
.map_err(|_| InstructionError::CallDepth)?;
// Verify the per-account instruction results
let (mut pre_sum, mut post_sum) = (0_u128, 0_u128);
let mut work = |index_in_instruction: usize, instruction_account: &InstructionAccount| {
let mut work = |_index_in_instruction: usize, instruction_account: &InstructionAccount| {
if instruction_account.index_in_transaction
< transaction_context.get_number_of_accounts()
{
@ -438,13 +457,18 @@ impl<'a> InvokeContext<'a> {
.get_key_of_account_at_index(instruction_account.index_in_transaction);
let account = transaction_context
.get_account_at_index(instruction_account.index_in_transaction);
let is_writable = if let Some(caller_write_privileges) = caller_write_privileges {
caller_write_privileges[index_in_instruction]
let is_writable = if before_instruction_context_push {
instruction_context
.try_borrow_account(
self.transaction_context,
instruction_account.index_in_caller,
)?
.is_writable()
} else {
instruction_account.is_writable
};
// Find the matching PreAccount
for pre_account in pre_accounts.iter_mut() {
for pre_account in self.pre_accounts.iter_mut() {
if key == pre_account.key() {
{
// Verify account has no outstanding references
@ -457,15 +481,15 @@ impl<'a> InvokeContext<'a> {
.verify(
program_id,
is_writable,
rent,
&self.rent,
&account,
timings,
&mut self.timings,
false,
do_support_realloc,
)
.map_err(|err| {
ic_logger_msg!(
log_collector,
self.log_collector,
"failed to verify account {}: {}",
key,
err
@ -502,7 +526,7 @@ impl<'a> InvokeContext<'a> {
instruction: Instruction,
signers: &[Pubkey],
) -> Result<(), InstructionError> {
let (instruction_accounts, caller_write_privileges, program_indices) =
let (instruction_accounts, program_indices) =
self.prepare_instruction(&instruction, signers)?;
let mut prev_account_sizes = Vec::with_capacity(instruction_accounts.len());
for instruction_account in instruction_accounts.iter() {
@ -519,7 +543,6 @@ impl<'a> InvokeContext<'a> {
self.process_instruction(
&instruction.data,
&instruction_accounts,
Some(&caller_write_privileges),
&program_indices,
&mut compute_units_consumed,
)?;
@ -556,11 +579,11 @@ impl<'a> InvokeContext<'a> {
&mut self,
instruction: &Instruction,
signers: &[Pubkey],
) -> Result<(Vec<InstructionAccount>, Vec<bool>, Vec<usize>), InstructionError> {
) -> Result<(Vec<InstructionAccount>, Vec<usize>), InstructionError> {
// Finds the index of each account in the instruction by its pubkey.
// Then normalizes / unifies the privileges of duplicate accounts.
// Note: This works like visit_each_account_once() and is an O(n^2) algorithm too.
let caller_keyed_accounts = self.get_keyed_accounts()?;
let instruction_context = self.transaction_context.get_current_instruction_context()?;
let mut deduplicated_instruction_accounts: Vec<InstructionAccount> = Vec::new();
let mut duplicate_indicies = Vec::with_capacity(instruction.accounts.len());
for account_meta in instruction.accounts.iter() {
@ -587,9 +610,8 @@ impl<'a> InvokeContext<'a> {
instruction_account.is_signer |= account_meta.is_signer;
instruction_account.is_writable |= account_meta.is_writable;
} else {
let index_in_caller = caller_keyed_accounts
.iter()
.position(|keyed_account| *keyed_account.unsigned_key() == account_meta.pubkey)
let index_in_caller = instruction_context
.find_index_of_account(self.transaction_context, &account_meta.pubkey)
.ok_or_else(|| {
ic_msg!(
self,
@ -598,6 +620,33 @@ impl<'a> InvokeContext<'a> {
);
InstructionError::MissingAccount
})?;
let borrowed_account = instruction_context
.try_borrow_account(self.transaction_context, index_in_caller)?;
// Readonly in caller cannot become writable in callee
if account_meta.is_writable && !borrowed_account.is_writable() {
ic_msg!(
self,
"{}'s writable privilege escalated",
borrowed_account.get_key(),
);
return Err(InstructionError::PrivilegeEscalation);
}
// To be signed in the callee,
// it must be either signed in the caller or by the program
if account_meta.is_signer
&& !(borrowed_account.is_signer()
|| signers.contains(borrowed_account.get_key()))
{
ic_msg!(
self,
"{}'s signer privilege escalated",
borrowed_account.get_key()
);
return Err(InstructionError::PrivilegeEscalation);
}
duplicate_indicies.push(deduplicated_instruction_accounts.len());
deduplicated_instruction_accounts.push(InstructionAccount {
index_in_transaction,
@ -612,66 +661,25 @@ impl<'a> InvokeContext<'a> {
.map(|duplicate_index| deduplicated_instruction_accounts[duplicate_index].clone())
.collect();
// Check for privilege escalation
let caller_write_privileges = instruction_accounts
.iter()
.map(|instruction_account| {
let keyed_account = &caller_keyed_accounts[instruction_account.index_in_caller];
// Readonly in caller cannot become writable in callee
if instruction_account.is_writable && !keyed_account.is_writable() {
ic_msg!(
self,
"{}'s writable privilege escalated",
keyed_account.unsigned_key(),
);
return Err(InstructionError::PrivilegeEscalation);
}
// To be signed in the callee,
// it must be either signed in the caller or by the program
if instruction_account.is_signer
&& !(keyed_account.signer_key().is_some()
|| signers.contains(keyed_account.unsigned_key()))
{
ic_msg!(
self,
"{}'s signer privilege escalated",
keyed_account.unsigned_key()
);
return Err(InstructionError::PrivilegeEscalation);
}
Ok(keyed_account.is_writable())
})
.collect::<Result<Vec<bool>, InstructionError>>()?;
// Find and validate executables / program accounts
let callee_program_id = instruction.program_id;
let program_account_index = caller_keyed_accounts
.iter()
.find(|keyed_account| &callee_program_id == keyed_account.unsigned_key())
.and_then(|_keyed_account| {
self.transaction_context
.find_index_of_program_account(&callee_program_id)
})
let program_account_index = instruction_context
.find_index_of_account(self.transaction_context, &callee_program_id)
.ok_or_else(|| {
ic_msg!(self, "Unknown program {}", callee_program_id);
InstructionError::MissingAccount
})?;
let program_account = self
.transaction_context
.get_account_at_index(program_account_index)
.borrow();
if !program_account.executable() {
let borrowed_program_account = instruction_context
.try_borrow_account(self.transaction_context, program_account_index)?;
if !borrowed_program_account.is_executable() {
ic_msg!(self, "Account {} is not executable", callee_program_id);
return Err(InstructionError::AccountNotExecutable);
}
let mut program_indices = vec![];
if program_account.owner() == &bpf_loader_upgradeable::id() {
if borrowed_program_account.get_owner() == &bpf_loader_upgradeable::id() {
if let UpgradeableLoaderState::Program {
programdata_address,
} = program_account.state()?
} = borrowed_program_account.get_state()?
{
if let Some(programdata_account_index) = self
.transaction_context
@ -695,21 +703,16 @@ impl<'a> InvokeContext<'a> {
return Err(InstructionError::MissingAccount);
}
}
program_indices.push(program_account_index);
program_indices.push(borrowed_program_account.get_index_in_transaction());
Ok((
instruction_accounts,
caller_write_privileges,
program_indices,
))
Ok((instruction_accounts, program_indices))
}
/// Processes a cross-program instruction and returns how many compute units were used
/// Processes an instruction and returns how many compute units were used
pub fn process_instruction(
&mut self,
instruction_data: &[u8],
instruction_accounts: &[InstructionAccount],
caller_write_privileges: Option<&[bool]>,
program_indices: &[usize],
compute_units_consumed: &mut u64,
) -> Result<(), InstructionError> {
@ -719,14 +722,17 @@ impl<'a> InvokeContext<'a> {
.map(|index| *self.transaction_context.get_key_of_account_at_index(*index))
.unwrap_or_else(native_loader::id);
let is_lowest_invocation_level = self.invoke_stack.is_empty();
let is_lowest_invocation_level = self
.transaction_context
.get_instruction_context_stack_height()
== 0;
if is_lowest_invocation_level {
if let Some(instruction_recorder) = &self.instruction_recorder {
instruction_recorder.borrow_mut().begin_next_recording();
}
} else {
// Verify the calling program hasn't misbehaved
self.verify_and_update(instruction_accounts, caller_write_privileges)?;
self.verify_and_update(instruction_accounts, true)?;
// Record instruction
if let Some(instruction_recorder) = &self.instruction_recorder {
@ -748,7 +754,7 @@ impl<'a> InvokeContext<'a> {
}
let result = self
.push(instruction_accounts, program_indices)
.push(instruction_accounts, program_indices, instruction_data)
.and_then(|_| {
self.return_data = (program_id, Vec::new());
let pre_remaining_units = self.compute_meter.borrow().get_remaining();
@ -761,12 +767,12 @@ impl<'a> InvokeContext<'a> {
if is_lowest_invocation_level {
self.verify(instruction_accounts, program_indices)
} else {
self.verify_and_update(instruction_accounts, None)
self.verify_and_update(instruction_accounts, false)
}
});
// Pop the invoke_stack to restore previous state
self.pop();
let _ = self.pop();
result
}
@ -775,14 +781,16 @@ impl<'a> InvokeContext<'a> {
&mut self,
instruction_data: &[u8],
) -> Result<(), InstructionError> {
let keyed_accounts = self.get_keyed_accounts()?;
let root_account = keyed_account_at_index(keyed_accounts, 0)
let instruction_context = self.transaction_context.get_current_instruction_context()?;
let borrowed_root_account = instruction_context
.try_borrow_account(self.transaction_context, 0)
.map_err(|_| InstructionError::UnsupportedProgramId)?;
let root_id = root_account.unsigned_key();
let owner_id = &root_account.owner()?;
let root_id = borrowed_root_account.get_key();
let owner_id = borrowed_root_account.get_owner();
if solana_sdk::native_loader::check_id(owner_id) {
for entry in self.builtin_programs {
if entry.program_id == *root_id {
drop(borrowed_root_account);
// Call the builtin program
return (entry.process_instruction)(
1, // root_id to be skipped
@ -792,6 +800,7 @@ impl<'a> InvokeContext<'a> {
}
}
if !self.feature_set.is_active(&remove_native_loader::id()) {
drop(borrowed_root_account);
let native_loader = NativeLoader::default();
// Call the program via the native loader
return native_loader.process_instruction(0, instruction_data, self);
@ -799,6 +808,7 @@ impl<'a> InvokeContext<'a> {
} else {
for entry in self.builtin_programs {
if entry.program_id == *owner_id {
drop(borrowed_root_account);
// Call the program via a builtin loader
return (entry.process_instruction)(
0, // no root_id was provided
@ -811,27 +821,6 @@ impl<'a> InvokeContext<'a> {
Err(InstructionError::UnsupportedProgramId)
}
/// Get the program ID of the currently executing program
pub fn get_caller(&self) -> Result<&Pubkey, InstructionError> {
self.invoke_stack
.last()
.and_then(|frame| frame.program_id())
.ok_or(InstructionError::CallDepth)
}
/// Get the owner of the currently executing program
pub fn get_loader(&self) -> Result<Pubkey, InstructionError> {
let frame = self
.invoke_stack
.last()
.ok_or(InstructionError::CallDepth)?;
let first_instruction_account = frame
.number_of_program_accounts
.checked_sub(1)
.ok_or(InstructionError::CallDepth)?;
frame.keyed_accounts[first_instruction_account].owner()
}
/// Removes the first keyed account
#[deprecated(
since = "1.9.0",
@ -857,17 +846,6 @@ impl<'a> InvokeContext<'a> {
.ok_or(InstructionError::CallDepth)
}
/// Get the list of keyed accounts without the chain of program accounts
///
/// Note: This only contains the `KeyedAccount`s passed by the caller.
pub fn get_instruction_keyed_accounts(&self) -> Result<&[KeyedAccount], InstructionError> {
let frame = self
.invoke_stack
.last()
.ok_or(InstructionError::CallDepth)?;
Ok(&frame.keyed_accounts[frame.number_of_program_accounts..])
}
/// Get this invocation's LogCollector
pub fn get_log_collector(&self) -> Option<Rc<RefCell<LogCollector>>> {
self.log_collector.clone()
@ -975,13 +953,13 @@ pub fn with_mock_invoke_context<R, F: FnMut(&mut InvokeContext) -> R>(
}];
let preparation =
prepare_mock_invoke_context(transaction_accounts, instruction_accounts, &program_indices);
let transaction_context = TransactionContext::new(
let mut transaction_context = TransactionContext::new(
preparation.transaction_accounts,
ComputeBudget::default().max_invoke_depth,
ComputeBudget::default().max_invoke_depth.saturating_add(1),
);
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
invoke_context
.push(&preparation.instruction_accounts, &program_indices)
.push(&preparation.instruction_accounts, &program_indices, &[])
.unwrap();
callback(&mut invoke_context)
}
@ -1003,15 +981,20 @@ pub fn mock_process_instruction_with_sysvars(
preparation
.transaction_accounts
.push((*loader_id, processor_account));
let transaction_context = TransactionContext::new(
let mut transaction_context = TransactionContext::new(
preparation.transaction_accounts,
ComputeBudget::default().max_invoke_depth,
ComputeBudget::default().max_invoke_depth.saturating_add(1),
);
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
invoke_context.sysvars = sysvars;
let result = invoke_context
.push(&preparation.instruction_accounts, &program_indices)
.push(
&preparation.instruction_accounts,
&program_indices,
instruction_data,
)
.and_then(|_| process_instruction(1, instruction_data, &mut invoke_context));
invoke_context.pop().unwrap();
assert_eq!(result, expected_result);
let mut transaction_accounts = transaction_context.deconstruct_without_keys().unwrap();
transaction_accounts.pop();
@ -1062,7 +1045,10 @@ mod tests {
use {
super::*,
serde::{Deserialize, Serialize},
solana_sdk::account::{ReadableAccount, WritableAccount},
solana_sdk::{
account::{ReadableAccount, WritableAccount},
keyed_account::keyed_account_at_index,
},
};
#[derive(Debug, Serialize, Deserialize)]
@ -1163,7 +1149,8 @@ mod tests {
data: &[u8],
invoke_context: &mut InvokeContext,
) -> Result<(), InstructionError> {
let program_id = invoke_context.get_caller()?;
let transaction_context = &invoke_context.transaction_context;
let program_id = transaction_context.get_program_key()?;
let keyed_accounts = invoke_context.get_keyed_accounts()?;
assert_eq!(
*program_id,
@ -1242,14 +1229,14 @@ mod tests {
is_writable: false,
});
}
let transaction_context = TransactionContext::new(accounts, MAX_DEPTH);
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let mut transaction_context = TransactionContext::new(accounts, MAX_DEPTH);
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
// Check call depth increases and has a limit
let mut depth_reached = 0;
for _ in 0..invoke_stack.len() {
if Err(InstructionError::CallDepth)
== invoke_context.push(&instruction_accounts, &[MAX_DEPTH + depth_reached])
== invoke_context.push(&instruction_accounts, &[MAX_DEPTH + depth_reached], &[])
{
break;
}
@ -1277,12 +1264,13 @@ mod tests {
];
// modify account owned by the program
transaction_context
invoke_context
.transaction_context
.get_account_at_index(owned_index)
.borrow_mut()
.data_as_mut_slice()[0] = (MAX_DEPTH + owned_index) as u8;
invoke_context
.verify_and_update(&instruction_accounts, None)
.verify_and_update(&instruction_accounts, false)
.unwrap();
assert_eq!(
invoke_context.pre_accounts[owned_index].data()[0],
@ -1290,25 +1278,28 @@ mod tests {
);
// modify account not owned by the program
let data = transaction_context
let data = invoke_context
.transaction_context
.get_account_at_index(not_owned_index)
.borrow_mut()
.data()[0];
transaction_context
invoke_context
.transaction_context
.get_account_at_index(not_owned_index)
.borrow_mut()
.data_as_mut_slice()[0] = (MAX_DEPTH + not_owned_index) as u8;
assert_eq!(
invoke_context.verify_and_update(&instruction_accounts, None),
invoke_context.verify_and_update(&instruction_accounts, false),
Err(InstructionError::ExternalAccountDataModified)
);
assert_eq!(invoke_context.pre_accounts[not_owned_index].data()[0], data);
transaction_context
invoke_context
.transaction_context
.get_account_at_index(not_owned_index)
.borrow_mut()
.data_as_mut_slice()[0] = data;
invoke_context.pop();
invoke_context.pop().unwrap();
}
}
@ -1317,20 +1308,14 @@ mod tests {
let accounts = vec![(solana_sdk::pubkey::new_rand(), AccountSharedData::default())];
let instruction_accounts = vec![];
let program_indices = vec![0];
let transaction_context = TransactionContext::new(accounts, 1);
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let mut transaction_context = TransactionContext::new(accounts, 1);
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
invoke_context
.push(&instruction_accounts, &program_indices)
.push(&instruction_accounts, &program_indices, &[])
.unwrap();
assert!(invoke_context
.verify(&instruction_accounts, &program_indices)
.is_ok());
let mut _borrowed = transaction_context.get_account_at_index(0).borrow();
assert_eq!(
invoke_context.verify(&instruction_accounts, &program_indices),
Err(InstructionError::AccountBorrowOutstanding)
);
}
#[test]
@ -1367,12 +1352,15 @@ mod tests {
is_writable: index_in_transaction < 2,
})
.collect::<Vec<_>>();
let transaction_context = TransactionContext::new(accounts, 2);
let mut invoke_context = InvokeContext::new_mock(&transaction_context, builtin_programs);
let mut transaction_context = TransactionContext::new(accounts, 2);
let mut invoke_context =
InvokeContext::new_mock(&mut transaction_context, builtin_programs);
// External modification tests
{
invoke_context.push(&instruction_accounts, &[4]).unwrap();
invoke_context
.push(&instruction_accounts, &[4], &[])
.unwrap();
let inner_instruction = Instruction::new_with_bincode(
callee_program_id,
&MockInstruction::NoopSuccess,
@ -1411,7 +1399,7 @@ mod tests {
.borrow_mut()
.data_as_mut_slice()[0] = 0;
invoke_context.pop();
invoke_context.pop().unwrap();
}
// Internal modification tests
@ -1432,18 +1420,22 @@ mod tests {
),
];
for case in cases {
invoke_context.push(&instruction_accounts, &[4]).unwrap();
invoke_context
.push(&instruction_accounts, &[4], &[])
.unwrap();
let inner_instruction =
Instruction::new_with_bincode(callee_program_id, &case.0, metas.clone());
assert_eq!(invoke_context.native_invoke(inner_instruction, &[]), case.1);
invoke_context.pop();
invoke_context.pop().unwrap();
}
// Compute unit consumption tests
let compute_units_to_consume = 10;
let expected_results = vec![Ok(()), Err(InstructionError::GenericError)];
for expected_result in expected_results {
invoke_context.push(&instruction_accounts, &[4]).unwrap();
invoke_context
.push(&instruction_accounts, &[4], &[])
.unwrap();
let inner_instruction = Instruction::new_with_bincode(
callee_program_id,
&MockInstruction::ConsumeComputeUnits {
@ -1452,16 +1444,14 @@ mod tests {
},
metas.clone(),
);
let (inner_instruction_accounts, caller_write_privileges, program_indices) =
invoke_context
.prepare_instruction(&inner_instruction, &[])
.unwrap();
let (inner_instruction_accounts, program_indices) = invoke_context
.prepare_instruction(&inner_instruction, &[])
.unwrap();
let mut compute_units_consumed = 0;
let result = invoke_context.process_instruction(
&inner_instruction.data,
&inner_instruction_accounts,
Some(&caller_write_privileges),
&program_indices,
&mut compute_units_consumed,
);
@ -1473,7 +1463,7 @@ mod tests {
assert_eq!(compute_units_consumed, compute_units_to_consume);
assert_eq!(result, expected_result);
invoke_context.pop();
invoke_context.pop().unwrap();
}
}
@ -1487,18 +1477,18 @@ mod tests {
let mut feature_set = FeatureSet::all_enabled();
feature_set.deactivate(&tx_wide_compute_cap::id());
feature_set.deactivate(&requestable_heap_size::id());
let transaction_context = TransactionContext::new(accounts, 1);
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let mut transaction_context = TransactionContext::new(accounts, 1);
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
invoke_context.feature_set = Arc::new(feature_set);
invoke_context.push(&[], &[0]).unwrap();
invoke_context.push(&[], &[0], &[]).unwrap();
assert_eq!(
*invoke_context.get_compute_budget(),
ComputeBudget::default()
);
invoke_context.pop();
invoke_context.pop().unwrap();
invoke_context.push(&[], &[1]).unwrap();
invoke_context.push(&[], &[1], &[]).unwrap();
let expected_compute_budget = ComputeBudget {
max_units: 500_000,
heap_size: Some(256_usize.saturating_mul(1024)),
@ -1508,13 +1498,13 @@ mod tests {
*invoke_context.get_compute_budget(),
expected_compute_budget
);
invoke_context.pop();
invoke_context.pop().unwrap();
invoke_context.push(&[], &[0]).unwrap();
invoke_context.push(&[], &[0], &[]).unwrap();
assert_eq!(
*invoke_context.get_compute_budget(),
ComputeBudget::default()
);
invoke_context.pop();
invoke_context.pop().unwrap();
}
}

View File

@ -170,7 +170,7 @@ impl NativeLoader {
invoke_context: &mut InvokeContext,
) -> Result<(), InstructionError> {
let (program_id, name_vec) = {
let program_id = invoke_context.get_caller()?;
let program_id = invoke_context.transaction_context.get_program_key()?;
let keyed_accounts = invoke_context.get_keyed_accounts()?;
let program = keyed_account_at_index(keyed_accounts, first_instruction_account)?;
if native_loader::id() != *program_id {

View File

@ -45,7 +45,7 @@ use {
solana_vote_program::vote_state::{VoteState, VoteStateVersions},
std::{
cell::RefCell,
collections::HashMap,
collections::HashSet,
convert::TryFrom,
fs::File,
io::{self, Read},
@ -100,57 +100,68 @@ pub fn builtin_process_instruction(
) -> Result<(), InstructionError> {
set_invoke_context(invoke_context);
let transaction_context = &invoke_context.transaction_context;
let instruction_context = transaction_context.get_current_instruction_context()?;
let indices_in_instruction = instruction_context.get_number_of_program_accounts()
..instruction_context.get_number_of_accounts();
let log_collector = invoke_context.get_log_collector();
let program_id = invoke_context.get_caller()?;
let program_id = transaction_context.get_program_key()?;
stable_log::program_invoke(&log_collector, program_id, invoke_context.invoke_depth());
// Skip the processor account
let keyed_accounts = &invoke_context.get_keyed_accounts()?[1..];
// Copy indices_in_instruction into a HashSet to ensure there are no duplicates
let deduplicated_indices: HashSet<usize> = indices_in_instruction.clone().collect();
// Copy all the accounts into a HashMap to ensure there are no duplicates
let mut accounts: HashMap<Pubkey, Account> = keyed_accounts
// Create copies of the accounts
let mut account_copies = deduplicated_indices
.iter()
.map(|ka| {
(
*ka.unsigned_key(),
Account::from(ka.account.borrow().clone()),
)
.map(|index_in_instruction| {
let borrowed_account = instruction_context
.try_borrow_account(transaction_context, *index_in_instruction)?;
Ok((
*borrowed_account.get_key(),
*borrowed_account.get_owner(),
borrowed_account.get_lamports(),
borrowed_account.get_data().to_vec(),
))
})
.collect();
.collect::<Result<Vec<_>, InstructionError>>()?;
// Create shared references to each account's lamports/data/owner
let account_refs: HashMap<_, _> = accounts
// Create shared references to account_copies
let account_refs: Vec<_> = account_copies
.iter_mut()
.map(|(key, account)| {
.map(|(key, owner, lamports, data)| {
(
*key,
(
Rc::new(RefCell::new(&mut account.lamports)),
Rc::new(RefCell::new(&mut account.data[..])),
&account.owner,
),
key,
owner,
Rc::new(RefCell::new(lamports)),
Rc::new(RefCell::new(data.as_mut())),
)
})
.collect();
// Create AccountInfos
let account_infos: Vec<AccountInfo> = keyed_accounts
.iter()
.map(|keyed_account| {
let key = keyed_account.unsigned_key();
let (lamports, data, owner) = &account_refs[key];
AccountInfo {
let account_infos = indices_in_instruction
.map(|index_in_instruction| {
let account_copy_index = deduplicated_indices
.iter()
.position(|index| *index == index_in_instruction)
.unwrap();
let (key, owner, lamports, data) = &account_refs[account_copy_index];
let borrowed_account = instruction_context
.try_borrow_account(transaction_context, index_in_instruction)?;
Ok(AccountInfo {
key,
is_signer: keyed_account.signer_key().is_some(),
is_writable: keyed_account.is_writable(),
is_signer: borrowed_account.is_signer(),
is_writable: borrowed_account.is_writable(),
lamports: lamports.clone(),
data: data.clone(),
owner,
executable: keyed_account.executable().unwrap(),
rent_epoch: keyed_account.rent_epoch().unwrap(),
}
executable: borrowed_account.is_executable(),
rent_epoch: borrowed_account.get_rent_epoch(),
})
})
.collect();
.collect::<Result<Vec<AccountInfo>, InstructionError>>()?;
// Execute the program
process_instruction(program_id, &account_infos, input).map_err(|err| {
@ -161,12 +172,16 @@ pub fn builtin_process_instruction(
stable_log::program_success(&log_collector, program_id);
// Commit AccountInfo changes back into KeyedAccounts
for keyed_account in keyed_accounts {
let mut account = keyed_account.account.borrow_mut();
let key = keyed_account.unsigned_key();
let (lamports, data, _owner) = &account_refs[key];
account.set_lamports(**lamports.borrow());
account.set_data(data.borrow().to_vec());
for (index_in_instruction, (_key, _owner, lamports, data)) in deduplicated_indices
.into_iter()
.zip(account_copies.into_iter())
{
let mut borrowed_account =
instruction_context.try_borrow_account(transaction_context, index_in_instruction)?;
if borrowed_account.is_writable() {
borrowed_account.set_lamports(lamports)?;
borrowed_account.set_data(&data)?;
}
}
Ok(())
@ -233,7 +248,10 @@ impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs {
let invoke_context = get_invoke_context();
let log_collector = invoke_context.get_log_collector();
let caller = *invoke_context.get_caller().expect("get_caller");
let caller = *invoke_context
.transaction_context
.get_program_key()
.unwrap();
stable_log::program_invoke(
&log_collector,
@ -245,7 +263,7 @@ impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs {
.iter()
.map(|seeds| Pubkey::create_program_address(seeds, &caller).unwrap())
.collect::<Vec<_>>();
let (instruction_accounts, caller_write_privileges, program_indices) = invoke_context
let (instruction_accounts, program_indices) = invoke_context
.prepare_instruction(instruction, &signers)
.unwrap();
@ -281,7 +299,6 @@ impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs {
.process_instruction(
&instruction.data,
&instruction_accounts,
Some(&caller_write_privileges),
&program_indices,
&mut compute_units_consumed,
)

View File

@ -209,12 +209,9 @@ fn bench_create_vm(bencher: &mut Bencher) {
.mock_set_remaining(BUDGET);
// Serialize account data
let keyed_accounts = invoke_context.get_keyed_accounts().unwrap();
let (mut serialized, account_lengths) = serialize_parameters(
&keyed_accounts[0].unsigned_key(),
&keyed_accounts[1].unsigned_key(),
&keyed_accounts[2..],
&[],
invoke_context.transaction_context,
invoke_context.transaction_context.get_current_instruction_context().unwrap(),
)
.unwrap();
@ -250,12 +247,9 @@ fn bench_instruction_count_tuner(_bencher: &mut Bencher) {
.mock_set_remaining(BUDGET);
// Serialize account data
let keyed_accounts = invoke_context.get_keyed_accounts().unwrap();
let (mut serialized, account_lengths) = serialize_parameters(
&keyed_accounts[0].unsigned_key(),
&keyed_accounts[1].unsigned_key(),
&keyed_accounts[2..],
&[],
invoke_context.transaction_context,
invoke_context.transaction_context.get_current_instruction_context().unwrap(),
)
.unwrap();

View File

@ -196,12 +196,9 @@ fn run_program(name: &str) -> u64 {
file.read_to_end(&mut data).unwrap();
let loader_id = bpf_loader::id();
with_mock_invoke_context(loader_id, 0, |invoke_context| {
let keyed_accounts = invoke_context.get_keyed_accounts().unwrap();
let (parameter_bytes, account_lengths) = serialize_parameters(
&keyed_accounts[0].unsigned_key(),
&keyed_accounts[1].unsigned_key(),
&keyed_accounts[2..],
&[],
invoke_context.transaction_context,
invoke_context.transaction_context.get_current_instruction_context().unwrap(),
)
.unwrap();
@ -227,7 +224,7 @@ fn run_program(name: &str) -> u64 {
let mut instruction_count = 0;
let mut tracer = None;
for i in 0..2 {
invoke_context.return_data = (*invoke_context.get_caller().unwrap(), Vec::new());
invoke_context.return_data = (*invoke_context.transaction_context.get_program_key().unwrap(), Vec::new());
let mut parameter_bytes = parameter_bytes.clone();
{
let mut vm = create_vm(
@ -278,10 +275,9 @@ fn run_program(name: &str) -> u64 {
tracer = Some(vm.get_tracer().clone());
}
}
let keyed_accounts = invoke_context.get_keyed_accounts().unwrap();
deserialize_parameters(
&loader_id,
&keyed_accounts[2..],
invoke_context.transaction_context,
invoke_context.transaction_context.get_current_instruction_context().unwrap(),
parameter_bytes.as_slice(),
&account_lengths,
true,

View File

@ -9,136 +9,123 @@ use {
solana_sdk::{
account::{Account, AccountSharedData},
bpf_loader,
keyed_account::KeyedAccount,
pubkey::Pubkey,
transaction_context::{InstructionAccount, TransactionContext},
},
std::cell::RefCell,
test::Bencher,
};
fn create_inputs() -> (
Pubkey,
Vec<Pubkey>,
Vec<RefCell<AccountSharedData>>,
Vec<u8>,
) {
fn create_inputs() -> TransactionContext {
let program_id = solana_sdk::pubkey::new_rand();
let dup_key = solana_sdk::pubkey::new_rand();
let dup_key2 = solana_sdk::pubkey::new_rand();
let keys = vec![
dup_key,
dup_key,
solana_sdk::pubkey::new_rand(),
solana_sdk::pubkey::new_rand(),
dup_key2,
dup_key2,
solana_sdk::pubkey::new_rand(),
solana_sdk::pubkey::new_rand(),
let transaction_accounts = vec![
(
program_id,
AccountSharedData::from(Account {
lamports: 0,
data: vec![],
owner: bpf_loader::id(),
executable: true,
rent_epoch: 0,
}),
),
(
solana_sdk::pubkey::new_rand(),
AccountSharedData::from(Account {
lamports: 1,
data: vec![1u8; 100000],
owner: bpf_loader::id(),
executable: false,
rent_epoch: 100,
}),
),
(
solana_sdk::pubkey::new_rand(),
AccountSharedData::from(Account {
lamports: 2,
data: vec![11u8; 100000],
owner: bpf_loader::id(),
executable: true,
rent_epoch: 200,
}),
),
(
solana_sdk::pubkey::new_rand(),
AccountSharedData::from(Account {
lamports: 3,
data: vec![],
owner: bpf_loader::id(),
executable: false,
rent_epoch: 3100,
}),
),
(
solana_sdk::pubkey::new_rand(),
AccountSharedData::from(Account {
lamports: 4,
data: vec![1u8; 100000],
owner: bpf_loader::id(),
executable: false,
rent_epoch: 100,
}),
),
(
solana_sdk::pubkey::new_rand(),
AccountSharedData::from(Account {
lamports: 5,
data: vec![11u8; 10000],
owner: bpf_loader::id(),
executable: true,
rent_epoch: 200,
}),
),
(
solana_sdk::pubkey::new_rand(),
AccountSharedData::from(Account {
lamports: 6,
data: vec![],
owner: bpf_loader::id(),
executable: false,
rent_epoch: 3100,
}),
),
];
let accounts = vec![
RefCell::new(AccountSharedData::from(Account {
lamports: 1,
data: vec![1u8, 2, 3, 4, 5],
owner: bpf_loader::id(),
executable: false,
rent_epoch: 100,
})),
// dup
RefCell::new(AccountSharedData::from(Account {
lamports: 1,
data: vec![1u8; 100000],
owner: bpf_loader::id(),
executable: false,
rent_epoch: 100,
})),
RefCell::new(AccountSharedData::from(Account {
lamports: 2,
data: vec![11u8; 100000],
owner: bpf_loader::id(),
executable: true,
rent_epoch: 200,
})),
RefCell::new(AccountSharedData::from(Account {
lamports: 3,
data: vec![],
owner: bpf_loader::id(),
executable: false,
rent_epoch: 3100,
})),
RefCell::new(AccountSharedData::from(Account {
lamports: 4,
data: vec![1u8; 100000],
owner: bpf_loader::id(),
executable: false,
rent_epoch: 100,
})),
// dup
RefCell::new(AccountSharedData::from(Account {
lamports: 4,
data: vec![1u8; 1000000],
owner: bpf_loader::id(),
executable: false,
rent_epoch: 100,
})),
RefCell::new(AccountSharedData::from(Account {
lamports: 5,
data: vec![11u8; 10000],
owner: bpf_loader::id(),
executable: true,
rent_epoch: 200,
})),
RefCell::new(AccountSharedData::from(Account {
lamports: 6,
data: vec![],
owner: bpf_loader::id(),
executable: false,
rent_epoch: 3100,
})),
];
let instruction_accounts = [1, 1, 2, 3, 4, 4, 5, 6]
.into_iter()
.enumerate()
.map(
|(index_in_instruction, index_in_transaction)| InstructionAccount {
index_in_caller: 1usize.saturating_add(index_in_instruction),
index_in_transaction,
is_signer: false,
is_writable: index_in_instruction >= 4,
},
)
.collect::<Vec<_>>();
let mut transaction_context = TransactionContext::new(transaction_accounts, 1);
let instruction_data = vec![1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
(program_id, keys, accounts, instruction_data)
transaction_context
.push(&[0], &instruction_accounts, &instruction_data)
.unwrap();
transaction_context
}
#[bench]
fn bench_serialize_unaligned(bencher: &mut Bencher) {
let (program_id, keys, accounts, instruction_data) = create_inputs();
let keyed_accounts: Vec<_> = keys
.iter()
.zip(&accounts)
.enumerate()
.map(|(i, (key, account))| {
if i <= accounts.len() / 2 {
KeyedAccount::new_readonly(key, false, account)
} else {
KeyedAccount::new(key, false, account)
}
})
.collect();
let transaction_context = create_inputs();
let instruction_context = transaction_context
.get_current_instruction_context()
.unwrap();
bencher.iter(|| {
let _ = serialize_parameters_unaligned(&program_id, &keyed_accounts, &instruction_data)
.unwrap();
let _ = serialize_parameters_unaligned(&transaction_context, instruction_context).unwrap();
});
}
#[bench]
fn bench_serialize_aligned(bencher: &mut Bencher) {
let (program_id, keys, accounts, instruction_data) = create_inputs();
let keyed_accounts: Vec<_> = keys
.iter()
.zip(&accounts)
.enumerate()
.map(|(i, (key, account))| {
if i <= accounts.len() / 2 {
KeyedAccount::new_readonly(key, false, account)
} else {
KeyedAccount::new(key, false, account)
}
})
.collect();
let transaction_context = create_inputs();
let instruction_context = transaction_context
.get_current_instruction_context()
.unwrap();
bencher.iter(|| {
let _ =
serialize_parameters_aligned(&program_id, &keyed_accounts, &instruction_data).unwrap();
let _ = serialize_parameters_aligned(&transaction_context, instruction_context).unwrap();
});
}

View File

@ -225,7 +225,7 @@ fn process_instruction_common(
use_jit: bool,
) -> Result<(), InstructionError> {
let log_collector = invoke_context.get_log_collector();
let program_id = invoke_context.get_caller()?;
let program_id = invoke_context.transaction_context.get_program_key()?;
let keyed_accounts = invoke_context.get_keyed_accounts()?;
let first_account = keyed_account_at_index(keyed_accounts, first_instruction_account)?;
@ -301,7 +301,7 @@ fn process_instruction_common(
use_jit,
false,
)?;
let program_id = invoke_context.get_caller()?;
let program_id = invoke_context.transaction_context.get_program_key()?;
invoke_context.add_executor(program_id, executor.clone());
executor
}
@ -342,7 +342,7 @@ fn process_loader_upgradeable_instruction(
use_jit: bool,
) -> Result<(), InstructionError> {
let log_collector = invoke_context.get_log_collector();
let program_id = invoke_context.get_caller()?;
let program_id = invoke_context.transaction_context.get_program_key()?;
let keyed_accounts = invoke_context.get_keyed_accounts()?;
match limited_deserialize(instruction_data)? {
@ -495,7 +495,7 @@ fn process_loader_upgradeable_instruction(
.accounts
.push(AccountMeta::new(*buffer.unsigned_key(), false));
let caller_program_id = invoke_context.get_caller()?;
let caller_program_id = invoke_context.transaction_context.get_program_key()?;
let signers = [&[new_program_id.as_ref(), &[bump_seed]]]
.iter()
.map(|seeds| Pubkey::create_program_address(*seeds, caller_program_id))
@ -892,7 +892,7 @@ fn process_loader_instruction(
invoke_context: &mut InvokeContext,
use_jit: bool,
) -> Result<(), InstructionError> {
let program_id = invoke_context.get_caller()?;
let program_id = invoke_context.transaction_context.get_program_key()?;
let keyed_accounts = invoke_context.get_keyed_accounts()?;
let program = keyed_account_at_index(keyed_accounts, first_instruction_account)?;
if program.owner()? != *program_id {
@ -973,8 +973,8 @@ impl Debug for BpfExecutor {
impl Executor for BpfExecutor {
fn execute<'a, 'b>(
&self,
first_instruction_account: usize,
instruction_data: &[u8],
_first_instruction_account: usize,
_instruction_data: &[u8],
invoke_context: &'a mut InvokeContext<'b>,
use_jit: bool,
) -> Result<(), InstructionError> {
@ -983,15 +983,12 @@ impl Executor for BpfExecutor {
let invoke_depth = invoke_context.invoke_depth();
let mut serialize_time = Measure::start("serialize");
let keyed_accounts = invoke_context.get_keyed_accounts()?;
let program = keyed_account_at_index(keyed_accounts, first_instruction_account)?;
let loader_id = program.owner()?;
let program_id = *program.unsigned_key();
let program_id = *invoke_context.transaction_context.get_program_key()?;
let (mut parameter_bytes, account_lengths) = serialize_parameters(
&loader_id,
&program_id,
&keyed_accounts[first_instruction_account + 1..],
instruction_data,
invoke_context.transaction_context,
invoke_context
.transaction_context
.get_current_instruction_context()?,
)?;
serialize_time.stop();
let mut create_vm_time = Measure::start("create_vm");
@ -1076,10 +1073,11 @@ impl Executor for BpfExecutor {
let mut deserialize_time = Measure::start("deserialize");
let execute_or_deserialize_result = execution_result.and_then(|_| {
let keyed_accounts = invoke_context.get_keyed_accounts()?;
deserialize_parameters(
&loader_id,
&keyed_accounts[first_instruction_account + 1..],
invoke_context.transaction_context,
invoke_context
.transaction_context
.get_current_instruction_context()?,
parameter_bytes.as_slice(),
&account_lengths,
invoke_context

View File

@ -2,58 +2,79 @@ use {
byteorder::{ByteOrder, LittleEndian, WriteBytesExt},
solana_rbpf::{aligned_memory::AlignedMemory, ebpf::HOST_ALIGN},
solana_sdk::{
account::{ReadableAccount, WritableAccount},
bpf_loader_deprecated,
entrypoint::{BPF_ALIGN_OF_U128, MAX_PERMITTED_DATA_INCREASE},
instruction::InstructionError,
keyed_account::KeyedAccount,
pubkey::Pubkey,
system_instruction::MAX_PERMITTED_DATA_LENGTH,
transaction_context::{InstructionContext, TransactionContext},
},
std::{io::prelude::*, mem::size_of},
};
/// Look for a duplicate account and return its position if found
pub fn is_dup(accounts: &[KeyedAccount], keyed_account: &KeyedAccount) -> (bool, usize) {
for (i, account) in accounts.iter().enumerate() {
if account == keyed_account {
return (true, i);
}
}
(false, 0)
pub fn is_duplicate(
instruction_context: &InstructionContext,
index_in_instruction: usize,
) -> Option<usize> {
let index_in_transaction = instruction_context.get_index_in_transaction(index_in_instruction);
(instruction_context.get_number_of_program_accounts()..index_in_instruction).position(
|index_in_instruction| {
instruction_context.get_index_in_transaction(index_in_instruction)
== index_in_transaction
},
)
}
pub fn serialize_parameters(
loader_id: &Pubkey,
program_id: &Pubkey,
keyed_accounts: &[KeyedAccount],
data: &[u8],
transaction_context: &TransactionContext,
instruction_context: &InstructionContext,
) -> Result<(AlignedMemory, Vec<usize>), InstructionError> {
if *loader_id == bpf_loader_deprecated::id() {
serialize_parameters_unaligned(program_id, keyed_accounts, data)
let is_loader_deprecated = *instruction_context
.try_borrow_program_account(transaction_context)?
.get_owner()
== bpf_loader_deprecated::id();
if is_loader_deprecated {
serialize_parameters_unaligned(transaction_context, instruction_context)
} else {
serialize_parameters_aligned(program_id, keyed_accounts, data)
serialize_parameters_aligned(transaction_context, instruction_context)
}
.and_then(|buffer| {
let account_lengths = keyed_accounts
.iter()
.map(|keyed_account| keyed_account.data_len())
let account_lengths = (instruction_context.get_number_of_program_accounts()
..instruction_context.get_number_of_accounts())
.map(|index_in_instruction| {
Ok(instruction_context
.try_borrow_account(transaction_context, index_in_instruction)?
.get_data()
.len())
})
.collect::<Result<Vec<usize>, InstructionError>>()?;
Ok((buffer, account_lengths))
})
}
pub fn deserialize_parameters(
loader_id: &Pubkey,
keyed_accounts: &[KeyedAccount],
transaction_context: &TransactionContext,
instruction_context: &InstructionContext,
buffer: &[u8],
account_lengths: &[usize],
do_support_realloc: bool,
) -> Result<(), InstructionError> {
if *loader_id == bpf_loader_deprecated::id() {
deserialize_parameters_unaligned(keyed_accounts, buffer, account_lengths)
let is_loader_deprecated = *instruction_context
.try_borrow_program_account(transaction_context)?
.get_owner()
== bpf_loader_deprecated::id();
if is_loader_deprecated {
deserialize_parameters_unaligned(transaction_context, instruction_context, buffer)
} else {
deserialize_parameters_aligned(keyed_accounts, buffer, account_lengths, do_support_realloc)
deserialize_parameters_aligned(
transaction_context,
instruction_context,
buffer,
account_lengths,
do_support_realloc,
)
}
}
@ -75,90 +96,108 @@ pub fn get_serialized_account_size_unaligned(
}
pub fn serialize_parameters_unaligned(
program_id: &Pubkey,
keyed_accounts: &[KeyedAccount],
instruction_data: &[u8],
transaction_context: &TransactionContext,
instruction_context: &InstructionContext,
) -> Result<AlignedMemory, InstructionError> {
// Calculate size in order to alloc once
let mut size = size_of::<u64>();
for (i, keyed_account) in keyed_accounts.iter().enumerate() {
let (is_dup, _) = is_dup(&keyed_accounts[..i], keyed_account);
for index_in_instruction in instruction_context.get_number_of_program_accounts()
..instruction_context.get_number_of_accounts()
{
let duplicate = is_duplicate(instruction_context, index_in_instruction);
size += 1; // dup
if !is_dup {
size += get_serialized_account_size_unaligned(keyed_account)?;
if duplicate.is_none() {
let data_len = instruction_context
.try_borrow_account(transaction_context, index_in_instruction)?
.get_data()
.len();
size += size_of::<u8>() // is_signer
+ size_of::<u8>() // is_writable
+ size_of::<Pubkey>() // key
+ size_of::<u64>() // lamports
+ size_of::<u64>() // data len
+ data_len // data
+ size_of::<Pubkey>() // owner
+ size_of::<u8>() // executable
+ size_of::<u64>(); // rent_epoch
}
}
size += size_of::<u64>() // instruction data len
+ instruction_data.len() // instruction data
+ instruction_context.get_instruction_data().len() // instruction data
+ size_of::<Pubkey>(); // program id
let mut v = AlignedMemory::new(size, HOST_ALIGN);
v.write_u64::<LittleEndian>(keyed_accounts.len() as u64)
v.write_u64::<LittleEndian>(instruction_context.get_number_of_instruction_accounts() as u64)
.map_err(|_| InstructionError::InvalidArgument)?;
for (i, keyed_account) in keyed_accounts.iter().enumerate() {
let (is_dup, position) = is_dup(&keyed_accounts[..i], keyed_account);
if is_dup {
for index_in_instruction in instruction_context.get_number_of_program_accounts()
..instruction_context.get_number_of_accounts()
{
let duplicate = is_duplicate(instruction_context, index_in_instruction);
if let Some(position) = duplicate {
v.write_u8(position as u8)
.map_err(|_| InstructionError::InvalidArgument)?;
} else {
let borrowed_account = instruction_context
.try_borrow_account(transaction_context, index_in_instruction)?;
v.write_u8(std::u8::MAX)
.map_err(|_| InstructionError::InvalidArgument)?;
v.write_u8(keyed_account.signer_key().is_some() as u8)
v.write_u8(borrowed_account.is_signer() as u8)
.map_err(|_| InstructionError::InvalidArgument)?;
v.write_u8(keyed_account.is_writable() as u8)
v.write_u8(borrowed_account.is_writable() as u8)
.map_err(|_| InstructionError::InvalidArgument)?;
v.write_all(keyed_account.unsigned_key().as_ref())
v.write_all(borrowed_account.get_key().as_ref())
.map_err(|_| InstructionError::InvalidArgument)?;
v.write_u64::<LittleEndian>(keyed_account.lamports()?)
v.write_u64::<LittleEndian>(borrowed_account.get_lamports())
.map_err(|_| InstructionError::InvalidArgument)?;
v.write_u64::<LittleEndian>(keyed_account.data_len()? as u64)
v.write_u64::<LittleEndian>(borrowed_account.get_data().len() as u64)
.map_err(|_| InstructionError::InvalidArgument)?;
v.write_all(keyed_account.try_account_ref()?.data())
v.write_all(borrowed_account.get_data())
.map_err(|_| InstructionError::InvalidArgument)?;
v.write_all(keyed_account.owner()?.as_ref())
v.write_all(borrowed_account.get_owner().as_ref())
.map_err(|_| InstructionError::InvalidArgument)?;
v.write_u8(keyed_account.executable()? as u8)
v.write_u8(borrowed_account.is_executable() as u8)
.map_err(|_| InstructionError::InvalidArgument)?;
v.write_u64::<LittleEndian>(keyed_account.rent_epoch()? as u64)
v.write_u64::<LittleEndian>(borrowed_account.get_rent_epoch() as u64)
.map_err(|_| InstructionError::InvalidArgument)?;
}
}
v.write_u64::<LittleEndian>(instruction_data.len() as u64)
v.write_u64::<LittleEndian>(instruction_context.get_instruction_data().len() as u64)
.map_err(|_| InstructionError::InvalidArgument)?;
v.write_all(instruction_data)
.map_err(|_| InstructionError::InvalidArgument)?;
v.write_all(program_id.as_ref())
v.write_all(instruction_context.get_instruction_data())
.map_err(|_| InstructionError::InvalidArgument)?;
v.write_all(
instruction_context
.try_borrow_program_account(transaction_context)?
.get_key()
.as_ref(),
)
.map_err(|_| InstructionError::InvalidArgument)?;
Ok(v)
}
pub fn deserialize_parameters_unaligned(
keyed_accounts: &[KeyedAccount],
transaction_context: &TransactionContext,
instruction_context: &InstructionContext,
buffer: &[u8],
account_lengths: &[usize],
) -> Result<(), InstructionError> {
let mut start = size_of::<u64>(); // number of accounts
for (i, (keyed_account, _pre_len)) in keyed_accounts
.iter()
.zip(account_lengths.iter())
.enumerate()
for index_in_instruction in instruction_context.get_number_of_program_accounts()
..instruction_context.get_number_of_accounts()
{
let (is_dup, _) = is_dup(&keyed_accounts[..i], keyed_account);
let duplicate = is_duplicate(instruction_context, index_in_instruction);
start += 1; // is_dup
if !is_dup {
if duplicate.is_none() {
let mut borrowed_account = instruction_context
.try_borrow_account(transaction_context, index_in_instruction)?;
start += size_of::<u8>(); // is_signer
start += size_of::<u8>(); // is_writable
start += size_of::<Pubkey>(); // key
keyed_account
.try_account_ref_mut()?
.set_lamports(LittleEndian::read_u64(&buffer[start..]));
let _ = borrowed_account.set_lamports(LittleEndian::read_u64(&buffer[start..]));
start += size_of::<u64>() // lamports
+ size_of::<u64>(); // data length
let end = start + keyed_account.data_len()?;
keyed_account
.try_account_ref_mut()?
.set_data_from_slice(&buffer[start..end]);
start += keyed_account.data_len()? // data
let end = start + borrowed_account.get_data().len();
let _ = borrowed_account.set_data(&buffer[start..end]);
start += borrowed_account.get_data().len() // data
+ size_of::<Pubkey>() // owner
+ size_of::<u8>() // executable
+ size_of::<u64>(); // rent_epoch
@ -167,77 +206,77 @@ pub fn deserialize_parameters_unaligned(
Ok(())
}
pub fn get_serialized_account_size_aligned(
keyed_account: &KeyedAccount,
) -> Result<usize, InstructionError> {
let data_len = keyed_account.data_len()?;
Ok(
size_of::<u8>() // is_signer
+ size_of::<u8>() // is_writable
+ size_of::<u8>() // executable
+ 4 // padding to 128-bit aligned
+ size_of::<Pubkey>() // key
+ size_of::<Pubkey>() // owner
+ size_of::<u64>() // lamports
+ size_of::<u64>() // data len
+ data_len
+ MAX_PERMITTED_DATA_INCREASE
+ (data_len as *const u8).align_offset(BPF_ALIGN_OF_U128)
+ size_of::<u64>(), // rent epoch
)
}
pub fn serialize_parameters_aligned(
program_id: &Pubkey,
keyed_accounts: &[KeyedAccount],
instruction_data: &[u8],
transaction_context: &TransactionContext,
instruction_context: &InstructionContext,
) -> Result<AlignedMemory, InstructionError> {
// Calculate size in order to alloc once
let mut size = size_of::<u64>();
for (i, keyed_account) in keyed_accounts.iter().enumerate() {
let (is_dup, _) = is_dup(&keyed_accounts[..i], keyed_account);
for index_in_instruction in instruction_context.get_number_of_program_accounts()
..instruction_context.get_number_of_accounts()
{
let duplicate = is_duplicate(instruction_context, index_in_instruction);
size += 1; // dup
if is_dup {
if duplicate.is_some() {
size += 7; // padding to 64-bit aligned
} else {
size += get_serialized_account_size_aligned(keyed_account)?;
let data_len = instruction_context
.try_borrow_account(transaction_context, index_in_instruction)?
.get_data()
.len();
size += size_of::<u8>() // is_signer
+ size_of::<u8>() // is_writable
+ size_of::<u8>() // executable
+ 4 // padding to 128-bit aligned
+ size_of::<Pubkey>() // key
+ size_of::<Pubkey>() // owner
+ size_of::<u64>() // lamports
+ size_of::<u64>() // data len
+ data_len
+ MAX_PERMITTED_DATA_INCREASE
+ (data_len as *const u8).align_offset(BPF_ALIGN_OF_U128)
+ size_of::<u64>(); // rent epoch
}
}
size += size_of::<u64>() // data len
+ instruction_data.len()
+ instruction_context.get_instruction_data().len()
+ size_of::<Pubkey>(); // program id;
let mut v = AlignedMemory::new(size, HOST_ALIGN);
// Serialize into the buffer
v.write_u64::<LittleEndian>(keyed_accounts.len() as u64)
v.write_u64::<LittleEndian>(instruction_context.get_number_of_instruction_accounts() as u64)
.map_err(|_| InstructionError::InvalidArgument)?;
for (i, keyed_account) in keyed_accounts.iter().enumerate() {
let (is_dup, position) = is_dup(&keyed_accounts[..i], keyed_account);
if is_dup {
for index_in_instruction in instruction_context.get_number_of_program_accounts()
..instruction_context.get_number_of_accounts()
{
let duplicate = is_duplicate(instruction_context, index_in_instruction);
if let Some(position) = duplicate {
v.write_u8(position as u8)
.map_err(|_| InstructionError::InvalidArgument)?;
v.write_all(&[0u8, 0, 0, 0, 0, 0, 0])
.map_err(|_| InstructionError::InvalidArgument)?; // 7 bytes of padding to make 64-bit aligned
} else {
let borrowed_account = instruction_context
.try_borrow_account(transaction_context, index_in_instruction)?;
v.write_u8(std::u8::MAX)
.map_err(|_| InstructionError::InvalidArgument)?;
v.write_u8(keyed_account.signer_key().is_some() as u8)
v.write_u8(borrowed_account.is_signer() as u8)
.map_err(|_| InstructionError::InvalidArgument)?;
v.write_u8(keyed_account.is_writable() as u8)
v.write_u8(borrowed_account.is_writable() as u8)
.map_err(|_| InstructionError::InvalidArgument)?;
v.write_u8(keyed_account.executable()? as u8)
v.write_u8(borrowed_account.is_executable() as u8)
.map_err(|_| InstructionError::InvalidArgument)?;
v.write_all(&[0u8, 0, 0, 0])
.map_err(|_| InstructionError::InvalidArgument)?; // 4 bytes of padding to make 128-bit aligned
v.write_all(keyed_account.unsigned_key().as_ref())
v.write_all(borrowed_account.get_key().as_ref())
.map_err(|_| InstructionError::InvalidArgument)?;
v.write_all(keyed_account.owner()?.as_ref())
v.write_all(borrowed_account.get_owner().as_ref())
.map_err(|_| InstructionError::InvalidArgument)?;
v.write_u64::<LittleEndian>(keyed_account.lamports()?)
v.write_u64::<LittleEndian>(borrowed_account.get_lamports())
.map_err(|_| InstructionError::InvalidArgument)?;
v.write_u64::<LittleEndian>(keyed_account.data_len()? as u64)
v.write_u64::<LittleEndian>(borrowed_account.get_data().len() as u64)
.map_err(|_| InstructionError::InvalidArgument)?;
v.write_all(keyed_account.try_account_ref()?.data())
v.write_all(borrowed_account.get_data())
.map_err(|_| InstructionError::InvalidArgument)?;
v.resize(
MAX_PERMITTED_DATA_INCREASE
@ -245,45 +284,51 @@ pub fn serialize_parameters_aligned(
0,
)
.map_err(|_| InstructionError::InvalidArgument)?;
v.write_u64::<LittleEndian>(keyed_account.rent_epoch()? as u64)
v.write_u64::<LittleEndian>(borrowed_account.get_rent_epoch() as u64)
.map_err(|_| InstructionError::InvalidArgument)?;
}
}
v.write_u64::<LittleEndian>(instruction_data.len() as u64)
v.write_u64::<LittleEndian>(instruction_context.get_instruction_data().len() as u64)
.map_err(|_| InstructionError::InvalidArgument)?;
v.write_all(instruction_data)
.map_err(|_| InstructionError::InvalidArgument)?;
v.write_all(program_id.as_ref())
v.write_all(instruction_context.get_instruction_data())
.map_err(|_| InstructionError::InvalidArgument)?;
v.write_all(
instruction_context
.try_borrow_program_account(transaction_context)?
.get_key()
.as_ref(),
)
.map_err(|_| InstructionError::InvalidArgument)?;
Ok(v)
}
pub fn deserialize_parameters_aligned(
keyed_accounts: &[KeyedAccount],
transaction_context: &TransactionContext,
instruction_context: &InstructionContext,
buffer: &[u8],
account_lengths: &[usize],
do_support_realloc: bool,
) -> Result<(), InstructionError> {
let mut start = size_of::<u64>(); // number of accounts
for (i, (keyed_account, pre_len)) in keyed_accounts
.iter()
for (index_in_instruction, pre_len) in (instruction_context.get_number_of_program_accounts()
..instruction_context.get_number_of_accounts())
.zip(account_lengths.iter())
.enumerate()
{
let (is_dup, _) = is_dup(&keyed_accounts[..i], keyed_account);
let duplicate = is_duplicate(instruction_context, index_in_instruction);
start += size_of::<u8>(); // position
if is_dup {
if duplicate.is_some() {
start += 7; // padding to 64-bit aligned
} else {
let mut account = keyed_account.try_account_ref_mut()?;
let mut borrowed_account = instruction_context
.try_borrow_account(transaction_context, index_in_instruction)?;
start += size_of::<u8>() // is_signer
+ size_of::<u8>() // is_writable
+ size_of::<u8>() // executable
+ 4 // padding to 128-bit aligned
+ size_of::<Pubkey>(); // key
account.copy_into_owner_from_slice(&buffer[start..start + size_of::<Pubkey>()]);
let _ = borrowed_account.set_owner(&buffer[start..start + size_of::<Pubkey>()]);
start += size_of::<Pubkey>(); // owner
account.set_lamports(LittleEndian::read_u64(&buffer[start..]));
let _ = borrowed_account.set_lamports(LittleEndian::read_u64(&buffer[start..]));
start += size_of::<u64>(); // lamports
let post_len = LittleEndian::read_u64(&buffer[start..]) as usize;
start += size_of::<u64>(); // data length
@ -303,7 +348,7 @@ pub fn deserialize_parameters_aligned(
}
data_end
};
account.set_data_from_slice(&buffer[start..data_end]);
let _ = borrowed_account.set_data(&buffer[start..data_end]);
start += *pre_len + MAX_PERMITTED_DATA_INCREASE; // data
start += (start as *const u8).align_offset(BPF_ALIGN_OF_U128);
start += size_of::<u64>(); // rent_epoch
@ -318,12 +363,11 @@ mod tests {
super::*,
solana_program_runtime::invoke_context::{prepare_mock_invoke_context, InvokeContext},
solana_sdk::{
account::{Account, AccountSharedData},
account::{Account, AccountSharedData, ReadableAccount},
account_info::AccountInfo,
bpf_loader,
entrypoint::deserialize,
instruction::AccountMeta,
transaction_context::TransactionContext,
},
std::{
cell::RefCell,
@ -407,71 +451,40 @@ mod tests {
}),
),
];
let instruction_accounts = vec![
AccountMeta {
pubkey: transaction_accounts[1].0,
let instruction_accounts = [1, 1, 2, 3, 4, 4, 5, 6]
.into_iter()
.enumerate()
.map(|(index_in_instruction, index_in_transaction)| AccountMeta {
pubkey: transaction_accounts[index_in_transaction].0,
is_signer: false,
is_writable: false,
},
AccountMeta {
pubkey: transaction_accounts[1].0,
is_signer: false,
is_writable: false,
},
AccountMeta {
pubkey: transaction_accounts[2].0,
is_signer: false,
is_writable: false,
},
AccountMeta {
pubkey: transaction_accounts[3].0,
is_signer: false,
is_writable: false,
},
AccountMeta {
pubkey: transaction_accounts[4].0,
is_signer: false,
is_writable: true,
},
AccountMeta {
pubkey: transaction_accounts[4].0,
is_signer: false,
is_writable: true,
},
AccountMeta {
pubkey: transaction_accounts[5].0,
is_signer: false,
is_writable: true,
},
AccountMeta {
pubkey: transaction_accounts[6].0,
is_signer: false,
is_writable: true,
},
];
is_writable: index_in_instruction >= 4,
})
.collect();
let instruction_data = vec![1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
let program_indices = [0];
let preparation = prepare_mock_invoke_context(
transaction_accounts.clone(),
transaction_accounts,
instruction_accounts,
&program_indices,
);
let transaction_context = TransactionContext::new(preparation.transaction_accounts, 1);
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let mut transaction_context = TransactionContext::new(preparation.transaction_accounts, 1);
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
invoke_context
.push(&preparation.instruction_accounts, &program_indices)
.push(
&preparation.instruction_accounts,
&program_indices,
&instruction_data,
)
.unwrap();
let instruction_context = invoke_context
.transaction_context
.get_current_instruction_context()
.unwrap();
// check serialize_parameters_aligned
let ser_keyed_accounts = invoke_context.get_keyed_accounts().unwrap();
let (mut serialized, account_lengths) = serialize_parameters(
&bpf_loader::id(),
&program_id,
&ser_keyed_accounts[1..],
&instruction_data,
)
.unwrap();
let (mut serialized, account_lengths) =
serialize_parameters(invoke_context.transaction_context, instruction_context).unwrap();
let (de_program_id, de_accounts, de_instruction_data) =
unsafe { deserialize(&mut serialized.as_slice_mut()[0] as *mut u8) };
@ -483,11 +496,14 @@ mod tests {
0
);
for account_info in de_accounts {
let account = &transaction_accounts
.iter()
.find(|(key, _account)| key == account_info.key)
.unwrap()
.1;
let index_in_transaction = invoke_context
.transaction_context
.find_index_of_account(account_info.key)
.unwrap();
let account = invoke_context
.transaction_context
.get_account_at_index(index_in_transaction)
.borrow();
assert_eq!(account.lamports(), account_info.lamports());
assert_eq!(account.data(), &account_info.data.borrow()[..]);
assert_eq!(account.owner(), account_info.owner);
@ -510,44 +526,51 @@ mod tests {
let de_keyed_accounts = invoke_context.get_keyed_accounts().unwrap();
deserialize_parameters(
&bpf_loader::id(),
&de_keyed_accounts[1..],
invoke_context.transaction_context,
instruction_context,
serialized.as_slice(),
&account_lengths,
true,
)
.unwrap();
for keyed_account in de_keyed_accounts {
let account = &transaction_accounts
.iter()
.find(|(key, _account)| key == keyed_account.unsigned_key())
.unwrap()
.1;
let index_in_transaction = invoke_context
.transaction_context
.find_index_of_account(keyed_account.unsigned_key())
.unwrap();
let account = invoke_context
.transaction_context
.get_account_at_index(index_in_transaction)
.borrow();
assert_eq!(account.executable(), keyed_account.executable().unwrap());
assert_eq!(account.rent_epoch(), keyed_account.rent_epoch().unwrap());
}
// check serialize_parameters_unaligned
let _ = invoke_context
.transaction_context
.get_current_instruction_context()
.unwrap()
.try_borrow_account(invoke_context.transaction_context, 0)
.unwrap()
.set_owner(bpf_loader_deprecated::id().as_ref());
let ser_keyed_accounts = invoke_context.get_keyed_accounts().unwrap();
let (mut serialized, account_lengths) = serialize_parameters(
&bpf_loader_deprecated::id(),
&program_id,
&ser_keyed_accounts[1..],
&instruction_data,
)
.unwrap();
let (mut serialized, account_lengths) =
serialize_parameters(invoke_context.transaction_context, instruction_context).unwrap();
let (de_program_id, de_accounts, de_instruction_data) =
unsafe { deserialize_unaligned(&mut serialized.as_slice_mut()[0] as *mut u8) };
assert_eq!(&program_id, de_program_id);
assert_eq!(instruction_data, de_instruction_data);
for account_info in de_accounts {
let account = &transaction_accounts
.iter()
.find(|(key, _account)| key == account_info.key)
.unwrap()
.1;
let index_in_transaction = invoke_context
.transaction_context
.find_index_of_account(account_info.key)
.unwrap();
let account = invoke_context
.transaction_context
.get_account_at_index(index_in_transaction)
.borrow();
assert_eq!(account.lamports(), account_info.lamports());
assert_eq!(account.data(), &account_info.data.borrow()[..]);
assert_eq!(account.owner(), account_info.owner);
@ -557,19 +580,22 @@ mod tests {
let de_keyed_accounts = invoke_context.get_keyed_accounts().unwrap();
deserialize_parameters(
&bpf_loader_deprecated::id(),
&de_keyed_accounts[1..],
invoke_context.transaction_context,
instruction_context,
serialized.as_slice(),
&account_lengths,
true,
)
.unwrap();
for keyed_account in de_keyed_accounts {
let account = &transaction_accounts
.iter()
.find(|(key, _account)| key == keyed_account.unsigned_key())
.unwrap()
.1;
let index_in_transaction = invoke_context
.transaction_context
.find_index_of_account(keyed_account.unsigned_key())
.unwrap();
let account = invoke_context
.transaction_context
.get_account_at_index(index_in_transaction)
.borrow();
assert_eq!(account.lamports(), keyed_account.lamports().unwrap());
assert_eq!(
account.data(),

View File

@ -246,7 +246,8 @@ pub fn bind_syscall_context_objects<'a, 'b>(
.is_active(&sol_log_data_syscall_enabled::id());
let loader_id = invoke_context
.get_loader()
.transaction_context
.get_loader_key()
.map_err(SyscallError::InstructionError)?;
let invoke_context = Rc::new(RefCell::new(invoke_context));
@ -581,7 +582,8 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallPanic<'a, 'b> {
let loader_id = question_mark!(
invoke_context
.get_loader()
.transaction_context
.get_loader_key()
.map_err(SyscallError::InstructionError),
result
);
@ -620,7 +622,8 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallLog<'a, 'b> {
let loader_id = question_mark!(
invoke_context
.get_loader()
.transaction_context
.get_loader_key()
.map_err(SyscallError::InstructionError),
result
);
@ -734,7 +737,8 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallLogPubkey<'a, 'b> {
let loader_id = question_mark!(
invoke_context
.get_loader()
.transaction_context
.get_loader_key()
.map_err(SyscallError::InstructionError),
result
);
@ -850,7 +854,8 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallCreateProgramAddress<'a, 'b> {
let loader_id = question_mark!(
invoke_context
.get_loader()
.transaction_context
.get_loader_key()
.map_err(SyscallError::InstructionError),
result
);
@ -909,7 +914,8 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallTryFindProgramAddress<'a, 'b> {
let loader_id = question_mark!(
invoke_context
.get_loader()
.transaction_context
.get_loader_key()
.map_err(SyscallError::InstructionError),
result
);
@ -983,7 +989,8 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallSha256<'a, 'b> {
let loader_id = question_mark!(
invoke_context
.get_loader()
.transaction_context
.get_loader_key()
.map_err(SyscallError::InstructionError),
result
);
@ -1064,7 +1071,8 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallGetClockSysvar<'a, 'b> {
);
let loader_id = question_mark!(
invoke_context
.get_loader()
.transaction_context
.get_loader_key()
.map_err(SyscallError::InstructionError),
result
);
@ -1100,7 +1108,8 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallGetEpochScheduleSysvar<'a, 'b> {
);
let loader_id = question_mark!(
invoke_context
.get_loader()
.transaction_context
.get_loader_key()
.map_err(SyscallError::InstructionError),
result
);
@ -1137,7 +1146,8 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallGetFeesSysvar<'a, 'b> {
);
let loader_id = question_mark!(
invoke_context
.get_loader()
.transaction_context
.get_loader_key()
.map_err(SyscallError::InstructionError),
result
);
@ -1173,7 +1183,8 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallGetRentSysvar<'a, 'b> {
);
let loader_id = question_mark!(
invoke_context
.get_loader()
.transaction_context
.get_loader_key()
.map_err(SyscallError::InstructionError),
result
);
@ -1216,7 +1227,8 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallKeccak256<'a, 'b> {
let loader_id = question_mark!(
invoke_context
.get_loader()
.transaction_context
.get_loader_key()
.map_err(SyscallError::InstructionError),
result
);
@ -1316,7 +1328,8 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallMemcpy<'a, 'b> {
let loader_id = question_mark!(
invoke_context
.get_loader()
.transaction_context
.get_loader_key()
.map_err(SyscallError::InstructionError),
result
);
@ -1360,7 +1373,8 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallMemmove<'a, 'b> {
let loader_id = question_mark!(
invoke_context
.get_loader()
.transaction_context
.get_loader_key()
.map_err(SyscallError::InstructionError),
result
);
@ -1404,7 +1418,8 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallMemcmp<'a, 'b> {
let loader_id = question_mark!(
invoke_context
.get_loader()
.transaction_context
.get_loader_key()
.map_err(SyscallError::InstructionError),
result
);
@ -1461,7 +1476,8 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallMemset<'a, 'b> {
let loader_id = question_mark!(
invoke_context
.get_loader()
.transaction_context
.get_loader_key()
.map_err(SyscallError::InstructionError),
result
);
@ -1503,7 +1519,8 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallSecp256k1Recover<'a, 'b> {
let loader_id = question_mark!(
invoke_context
.get_loader()
.transaction_context
.get_loader_key()
.map_err(SyscallError::InstructionError),
result
);
@ -1608,7 +1625,8 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallBlake3<'a, 'b> {
let loader_id = question_mark!(
invoke_context
.get_loader()
.transaction_context
.get_loader_key()
.map_err(SyscallError::InstructionError),
result
);
@ -2202,14 +2220,10 @@ fn get_translated_accounts<'a, T, F>(
where
F: Fn(&T, &InvokeContext) -> Result<CallerAccount<'a>, EbpfError<BpfError>>,
{
let keyed_accounts = invoke_context
.get_keyed_accounts()
let instruction_context = invoke_context
.transaction_context
.get_current_instruction_context()
.map_err(SyscallError::InstructionError)?;
let number_of_program_accounts = keyed_accounts.len()
- invoke_context
.get_instruction_keyed_accounts()
.map_err(SyscallError::InstructionError)?
.len();
let mut accounts = Vec::with_capacity(instruction_accounts.len().saturating_add(1));
let program_account_index = program_indices
@ -2245,10 +2259,8 @@ where
let caller_account = if instruction_account.is_writable {
let orig_data_len_index = instruction_account
.index_in_caller
.saturating_sub(number_of_program_accounts);
if keyed_accounts[instruction_account.index_in_caller].unsigned_key() == account_key
&& orig_data_len_index < orig_data_lens.len()
{
.saturating_sub(instruction_context.get_number_of_program_accounts());
if orig_data_len_index < orig_data_lens.len() {
caller_account.original_data_len = orig_data_lens[orig_data_len_index];
} else {
ic_msg!(
@ -2351,7 +2363,8 @@ fn call<'a, 'b: 'a>(
// Translate and verify caller's data
let loader_id = invoke_context
.get_loader()
.transaction_context
.get_loader_key()
.map_err(SyscallError::InstructionError)?;
let instruction = syscall.translate_instruction(
&loader_id,
@ -2360,7 +2373,8 @@ fn call<'a, 'b: 'a>(
*invoke_context,
)?;
let caller_program_id = invoke_context
.get_caller()
.transaction_context
.get_program_key()
.map_err(SyscallError::InstructionError)?;
let signers = syscall.translate_signers(
&loader_id,
@ -2369,7 +2383,7 @@ fn call<'a, 'b: 'a>(
signers_seeds_len,
memory_mapping,
)?;
let (instruction_accounts, caller_write_privileges, program_indices) = invoke_context
let (instruction_accounts, program_indices) = invoke_context
.prepare_instruction(&instruction, &signers)
.map_err(SyscallError::InstructionError)?;
check_authorized_program(&instruction.program_id, &instruction.data, *invoke_context)?;
@ -2389,7 +2403,6 @@ fn call<'a, 'b: 'a>(
.process_instruction(
&instruction.data,
&instruction_accounts,
Some(&caller_write_privileges),
&program_indices,
&mut compute_units_consumed,
)
@ -2476,7 +2489,8 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallSetReturnData<'a, 'b> {
);
let loader_id = question_mark!(
invoke_context
.get_loader()
.transaction_context
.get_loader_key()
.map_err(SyscallError::InstructionError),
result
);
@ -2506,7 +2520,8 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallSetReturnData<'a, 'b> {
};
let program_id = question_mark!(
invoke_context
.get_caller()
.transaction_context
.get_program_key()
.map_err(SyscallError::InstructionError),
result
);
@ -2538,7 +2553,8 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallGetReturnData<'a, 'b> {
);
let loader_id = question_mark!(
invoke_context
.get_loader()
.transaction_context
.get_loader_key()
.map_err(SyscallError::InstructionError),
result
);
@ -2605,7 +2621,8 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallLogData<'a, 'b> {
);
let loader_id = question_mark!(
invoke_context
.get_loader()
.transaction_context
.get_loader_key()
.map_err(SyscallError::InstructionError),
result
);
@ -2984,12 +3001,12 @@ mod tests {
#[should_panic(expected = "UserError(SyscallError(Panic(\"Gaggablaghblagh!\", 42, 84)))")]
fn test_syscall_sol_panic() {
let program_id = Pubkey::new_unique();
let transaction_context = TransactionContext::new(
let mut transaction_context = TransactionContext::new(
vec![(program_id, AccountSharedData::new(0, 0, &bpf_loader::id()))],
1,
);
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
invoke_context.push(&[], &[0]).unwrap();
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
invoke_context.push(&[], &[0], &[]).unwrap();
let mut syscall_panic = SyscallPanic {
invoke_context: Rc::new(RefCell::new(&mut invoke_context)),
};
@ -3057,12 +3074,12 @@ mod tests {
#[test]
fn test_syscall_sol_log() {
let program_id = Pubkey::new_unique();
let transaction_context = TransactionContext::new(
let mut transaction_context = TransactionContext::new(
vec![(program_id, AccountSharedData::new(0, 0, &bpf_loader::id()))],
1,
);
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
invoke_context.push(&[], &[0]).unwrap();
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
invoke_context.push(&[], &[0], &[]).unwrap();
let mut syscall_sol_log = SyscallLog {
invoke_context: Rc::new(RefCell::new(&mut invoke_context)),
};
@ -3157,12 +3174,12 @@ mod tests {
#[test]
fn test_syscall_sol_log_u64() {
let program_id = Pubkey::new_unique();
let transaction_context = TransactionContext::new(
let mut transaction_context = TransactionContext::new(
vec![(program_id, AccountSharedData::new(0, 0, &bpf_loader::id()))],
1,
);
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
invoke_context.push(&[], &[0]).unwrap();
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
invoke_context.push(&[], &[0], &[]).unwrap();
let cost = invoke_context.get_compute_budget().log_64_units;
let mut syscall_sol_log_u64 = SyscallLogU64 {
invoke_context: Rc::new(RefCell::new(&mut invoke_context)),
@ -3195,12 +3212,12 @@ mod tests {
#[test]
fn test_syscall_sol_pubkey() {
let program_id = Pubkey::new_unique();
let transaction_context = TransactionContext::new(
let mut transaction_context = TransactionContext::new(
vec![(program_id, AccountSharedData::new(0, 0, &bpf_loader::id()))],
1,
);
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
invoke_context.push(&[], &[0]).unwrap();
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
invoke_context.push(&[], &[0], &[]).unwrap();
let cost = invoke_context.get_compute_budget().log_pubkey_units;
let mut syscall_sol_pubkey = SyscallLogPubkey {
invoke_context: Rc::new(RefCell::new(&mut invoke_context)),
@ -3403,15 +3420,15 @@ mod tests {
fn test_syscall_sha256() {
let config = Config::default();
let program_id = Pubkey::new_unique();
let transaction_context = TransactionContext::new(
let mut transaction_context = TransactionContext::new(
vec![(
program_id,
AccountSharedData::new(0, 0, &bpf_loader_deprecated::id()),
)],
1,
);
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
invoke_context.push(&[], &[0]).unwrap();
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
invoke_context.push(&[], &[0], &[]).unwrap();
let bytes1 = "Gaggablaghblagh!";
let bytes2 = "flurbos";
@ -3570,13 +3587,13 @@ mod tests {
(sysvar::rent::id(), data_rent),
];
let program_id = Pubkey::new_unique();
let transaction_context = TransactionContext::new(
let mut transaction_context = TransactionContext::new(
vec![(program_id, AccountSharedData::new(0, 0, &bpf_loader::id()))],
1,
);
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
invoke_context.sysvars = &sysvars;
invoke_context.push(&[], &[0]).unwrap();
invoke_context.push(&[], &[0], &[]).unwrap();
// Test clock sysvar
{
@ -3820,12 +3837,12 @@ mod tests {
// These tests duplicate the direct tests in solana_program::pubkey
let program_id = Pubkey::new_unique();
let transaction_context = TransactionContext::new(
let mut transaction_context = TransactionContext::new(
vec![(program_id, AccountSharedData::new(0, 0, &bpf_loader::id()))],
1,
);
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
invoke_context.push(&[], &[0]).unwrap();
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
invoke_context.push(&[], &[0], &[]).unwrap();
let address = bpf_loader_upgradeable::id();
let exceeded_seed = &[127; MAX_SEED_LEN + 1];
@ -3932,12 +3949,12 @@ mod tests {
#[test]
fn test_find_program_address() {
let program_id = Pubkey::new_unique();
let transaction_context = TransactionContext::new(
let mut transaction_context = TransactionContext::new(
vec![(program_id, AccountSharedData::new(0, 0, &bpf_loader::id()))],
1,
);
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
invoke_context.push(&[], &[0]).unwrap();
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
invoke_context.push(&[], &[0], &[]).unwrap();
let cost = invoke_context
.get_compute_budget()
.create_program_address_units;

View File

@ -4999,8 +4999,8 @@ mod tests {
#[test]
fn test_merge() {
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
let stake_pubkey = solana_sdk::pubkey::new_rand();
let source_stake_pubkey = solana_sdk::pubkey::new_rand();
let authorized_pubkey = solana_sdk::pubkey::new_rand();
@ -5110,8 +5110,8 @@ mod tests {
#[test]
fn test_merge_self_fails() {
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
let stake_address = Pubkey::new_unique();
let authority_pubkey = Pubkey::new_unique();
let signers = HashSet::from_iter(vec![authority_pubkey]);
@ -5156,8 +5156,8 @@ mod tests {
#[test]
fn test_merge_incorrect_authorized_staker() {
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
let stake_pubkey = solana_sdk::pubkey::new_rand();
let source_stake_pubkey = solana_sdk::pubkey::new_rand();
let authorized_pubkey = solana_sdk::pubkey::new_rand();
@ -5226,8 +5226,8 @@ mod tests {
#[test]
fn test_merge_invalid_account_data() {
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
let stake_pubkey = solana_sdk::pubkey::new_rand();
let source_stake_pubkey = solana_sdk::pubkey::new_rand();
let authorized_pubkey = solana_sdk::pubkey::new_rand();
@ -5277,8 +5277,8 @@ mod tests {
#[test]
fn test_merge_fake_stake_source() {
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
let stake_pubkey = solana_sdk::pubkey::new_rand();
let source_stake_pubkey = solana_sdk::pubkey::new_rand();
let authorized_pubkey = solana_sdk::pubkey::new_rand();
@ -5320,8 +5320,8 @@ mod tests {
#[test]
fn test_merge_active_stake() {
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
let base_lamports = 4242424242;
let stake_address = Pubkey::new_unique();
let source_address = Pubkey::new_unique();
@ -5943,8 +5943,8 @@ mod tests {
#[test]
fn test_things_can_merge() {
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
let good_stake = Stake {
credits_observed: 4242,
delegation: Delegation {
@ -6042,8 +6042,8 @@ mod tests {
#[test]
fn test_metas_can_merge_pre_v4() {
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
// Identical Metas can merge
assert!(MergeKind::metas_can_merge(
&invoke_context,
@ -6129,8 +6129,8 @@ mod tests {
#[test]
fn test_metas_can_merge_v4() {
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
// Identical Metas can merge
assert!(MergeKind::metas_can_merge(
&invoke_context,
@ -6276,8 +6276,8 @@ mod tests {
#[test]
fn test_merge_kind_get_if_mergeable() {
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
let authority_pubkey = Pubkey::new_unique();
let initial_lamports = 4242424242;
let rent = Rent::default();
@ -6509,8 +6509,8 @@ mod tests {
#[test]
fn test_merge_kind_merge() {
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
let lamports = 424242;
let meta = Meta {
rent_exempt_reserve: 42,
@ -6588,8 +6588,8 @@ mod tests {
#[test]
fn test_active_stake_merge() {
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
let delegation_a = 4_242_424_242u64;
let delegation_b = 6_200_000_000u64;
let credits_a = 124_521_000u64;

View File

@ -212,17 +212,21 @@ native machine code before execting it in the virtual machine.",
let program_indices = [0, 1];
let preparation =
prepare_mock_invoke_context(transaction_accounts, instruction_accounts, &program_indices);
let transaction_context = TransactionContext::new(preparation.transaction_accounts, 1);
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let mut transaction_context = TransactionContext::new(preparation.transaction_accounts, 1);
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
invoke_context
.push(&preparation.instruction_accounts, &program_indices)
.push(
&preparation.instruction_accounts,
&program_indices,
&instruction_data,
)
.unwrap();
let keyed_accounts = invoke_context.get_keyed_accounts().unwrap();
let (mut parameter_bytes, account_lengths) = serialize_parameters(
keyed_accounts[0].unsigned_key(),
keyed_accounts[1].unsigned_key(),
&keyed_accounts[2..],
&instruction_data,
invoke_context.transaction_context,
invoke_context
.transaction_context
.get_current_instruction_context()
.unwrap(),
)
.unwrap();
let compute_meter = invoke_context.get_compute_meter();

View File

@ -3561,9 +3561,9 @@ impl Bank {
let mut transaction_accounts = Vec::new();
std::mem::swap(&mut loaded_transaction.accounts, &mut transaction_accounts);
let transaction_context = TransactionContext::new(
let mut transaction_context = TransactionContext::new(
transaction_accounts,
compute_budget.max_invoke_depth,
compute_budget.max_invoke_depth.saturating_add(1),
);
let instruction_recorder = if enable_cpi_recording {
@ -3588,7 +3588,7 @@ impl Bank {
&self.builtin_programs.vec,
legacy_message,
&loaded_transaction.program_indices,
&transaction_context,
&mut transaction_context,
self.rent_collector.rent,
log_collector.clone(),
executors.clone(),
@ -10401,7 +10401,7 @@ pub(crate) mod tests {
_instruction_data: &[u8],
invoke_context: &mut InvokeContext,
) -> std::result::Result<(), InstructionError> {
let program_id = invoke_context.get_caller()?;
let program_id = invoke_context.transaction_context.get_program_key()?;
if mock_vote_program_id() != *program_id {
return Err(InstructionError::IncorrectProgramId);
}

View File

@ -19,12 +19,12 @@ fn process_instruction_with_program_logging(
invoke_context: &mut InvokeContext,
) -> Result<(), InstructionError> {
let logger = invoke_context.get_log_collector();
let program_id = invoke_context.get_caller()?;
let program_id = invoke_context.transaction_context.get_program_key()?;
stable_log::program_invoke(&logger, program_id, invoke_context.invoke_depth());
let result = process_instruction(first_instruction_account, instruction_data, invoke_context);
let program_id = invoke_context.get_caller()?;
let program_id = invoke_context.transaction_context.get_program_key()?;
match &result {
Ok(()) => stable_log::program_success(&logger, program_id),
Err(err) => stable_log::program_failure(&logger, program_id, err),

View File

@ -53,7 +53,7 @@ impl MessageProcessor {
builtin_programs: &[BuiltinProgram],
message: &Message,
program_indices: &[Vec<usize>],
transaction_context: &TransactionContext,
transaction_context: &mut TransactionContext,
rent: Rent,
log_collector: Option<Rc<RefCell<LogCollector>>>,
executors: Rc<RefCell<Executors>>,
@ -132,7 +132,6 @@ impl MessageProcessor {
let result = invoke_context.process_instruction(
&instruction.data,
&instruction_accounts,
None,
program_indices,
&mut compute_units_consumed,
);
@ -242,7 +241,7 @@ mod tests {
create_loadable_account_for_test("mock_system_program"),
),
];
let transaction_context = TransactionContext::new(accounts, 1);
let mut transaction_context = TransactionContext::new(accounts, 1);
let program_indices = vec![vec![2]];
let executors = Rc::new(RefCell::new(Executors::default()));
let account_metas = vec![
@ -262,7 +261,7 @@ mod tests {
builtin_programs,
&message,
&program_indices,
&transaction_context,
&mut transaction_context,
rent_collector.rent,
None,
executors.clone(),
@ -303,7 +302,7 @@ mod tests {
builtin_programs,
&message,
&program_indices,
&transaction_context,
&mut transaction_context,
rent_collector.rent,
None,
executors.clone(),
@ -336,7 +335,7 @@ mod tests {
builtin_programs,
&message,
&program_indices,
&transaction_context,
&mut transaction_context,
rent_collector.rent,
None,
executors,
@ -458,7 +457,7 @@ mod tests {
create_loadable_account_for_test("mock_system_program"),
),
];
let transaction_context = TransactionContext::new(accounts, 1);
let mut transaction_context = TransactionContext::new(accounts, 1);
let program_indices = vec![vec![2]];
let executors = Rc::new(RefCell::new(Executors::default()));
let account_metas = vec![
@ -480,7 +479,7 @@ mod tests {
builtin_programs,
&message,
&program_indices,
&transaction_context,
&mut transaction_context,
rent_collector.rent,
None,
executors.clone(),
@ -514,7 +513,7 @@ mod tests {
builtin_programs,
&message,
&program_indices,
&transaction_context,
&mut transaction_context,
rent_collector.rent,
None,
executors.clone(),
@ -545,7 +544,7 @@ mod tests {
builtin_programs,
&message,
&program_indices,
&transaction_context,
&mut transaction_context,
rent_collector.rent,
None,
executors,
@ -602,7 +601,7 @@ mod tests {
(secp256k1_program::id(), secp256k1_account),
(mock_program_id, mock_program_account),
];
let transaction_context = TransactionContext::new(accounts, 1);
let mut transaction_context = TransactionContext::new(accounts, 1);
let message = Message::new(
&[
@ -618,7 +617,7 @@ mod tests {
builtin_programs,
&message,
&[vec![0], vec![1]],
&transaction_context,
&mut transaction_context,
RentCollector::default().rent,
None,
Rc::new(RefCell::new(Executors::default())),

View File

@ -334,8 +334,8 @@ mod test {
where
F: FnMut(&mut InvokeContext, &KeyedAccount),
{
let transaction_context = TransactionContext::new(Vec::new(), 1);
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
let pubkey = Pubkey::new_unique();
let account = create_account(lamports);
let keyed_account = KeyedAccount::new(&pubkey, signer, &account);

View File

@ -675,8 +675,8 @@ mod tests {
#[test]
fn test_address_create_with_seed_mismatch() {
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
let from = Pubkey::new_unique();
let seed = "dull boy";
let to = Pubkey::new_unique();
@ -690,8 +690,8 @@ mod tests {
#[test]
fn test_create_account_with_seed_missing_sig() {
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
let new_owner = Pubkey::new(&[9; 32]);
let from = Pubkey::new_unique();
let seed = "dull boy";
@ -721,8 +721,8 @@ mod tests {
#[test]
fn test_create_with_zero_lamports() {
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
// create account with zero lamports transferred
let new_owner = Pubkey::new(&[9; 32]);
let from = Pubkey::new_unique();
@ -756,8 +756,8 @@ mod tests {
#[test]
fn test_create_negative_lamports() {
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
// Attempt to create account with more lamports than remaining in from_account
let new_owner = Pubkey::new(&[9; 32]);
let from = Pubkey::new_unique();
@ -781,8 +781,8 @@ mod tests {
#[test]
fn test_request_more_than_allowed_data_length() {
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
let from_account = RefCell::new(AccountSharedData::new(100, 0, &system_program::id()));
let from = Pubkey::new_unique();
let to_account = RefCell::new(AccountSharedData::new(0, 0, &system_program::id()));
@ -829,8 +829,8 @@ mod tests {
#[test]
fn test_create_already_in_use() {
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
// Attempt to create system account in account already owned by another program
let new_owner = Pubkey::new(&[9; 32]);
let from = Pubkey::new_unique();
@ -898,8 +898,8 @@ mod tests {
#[test]
fn test_create_unsigned() {
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
// Attempt to create an account without signing the transfer
let new_owner = Pubkey::new(&[9; 32]);
let from = Pubkey::new_unique();
@ -954,8 +954,8 @@ mod tests {
#[test]
fn test_create_sysvar_invalid_id_with_feature() {
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
// Attempt to create system account in account already owned by another program
let from = Pubkey::new_unique();
let from_account = RefCell::new(AccountSharedData::new(100, 0, &system_program::id()));
@ -989,8 +989,8 @@ mod tests {
feature_set
.inactive
.insert(feature_set::rent_for_sysvars::id());
let transaction_context = TransactionContext::new(Vec::new(), 1);
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
invoke_context.feature_set = Arc::new(feature_set);
// Attempt to create system account in account already owned by another program
let from = Pubkey::new_unique();
@ -1017,8 +1017,8 @@ mod tests {
#[test]
fn test_create_data_populated() {
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
// Attempt to create system account in account with populated data
let new_owner = Pubkey::new(&[9; 32]);
let from = Pubkey::new_unique();
@ -1051,8 +1051,8 @@ mod tests {
#[test]
fn test_create_from_account_is_nonce_fail() {
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
let nonce = Pubkey::new_unique();
let nonce_account = RefCell::new(
AccountSharedData::new_data(
@ -1090,8 +1090,8 @@ mod tests {
#[test]
fn test_assign() {
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
let new_owner = Pubkey::new(&[9; 32]);
let pubkey = Pubkey::new_unique();
let mut account = AccountSharedData::new(100, 0, &system_program::id());
@ -1133,8 +1133,8 @@ mod tests {
#[test]
fn test_assign_to_sysvar_with_feature() {
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
let new_owner = sysvar::id();
let from = Pubkey::new_unique();
let mut from_account = AccountSharedData::new(100, 0, &system_program::id());
@ -1160,8 +1160,8 @@ mod tests {
feature_set
.inactive
.insert(feature_set::rent_for_sysvars::id());
let transaction_context = TransactionContext::new(Vec::new(), 1);
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
invoke_context.feature_set = Arc::new(feature_set);
let new_owner = sysvar::id();
let from = Pubkey::new_unique();
@ -1212,8 +1212,8 @@ mod tests {
#[test]
fn test_transfer_lamports() {
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
let from = Pubkey::new_unique();
let from_account = RefCell::new(AccountSharedData::new(100, 0, &Pubkey::new(&[2; 32]))); // account owner should not matter
let to = Pubkey::new(&[3; 32]);
@ -1251,8 +1251,8 @@ mod tests {
#[test]
fn test_transfer_with_seed() {
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
let base = Pubkey::new_unique();
let base_account = RefCell::new(AccountSharedData::new(100, 0, &Pubkey::new(&[2; 32]))); // account owner should not matter
let from_base_keyed_account = KeyedAccount::new(&base, true, &base_account);
@ -1312,8 +1312,8 @@ mod tests {
#[test]
fn test_transfer_lamports_from_nonce_account_fail() {
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
let from = Pubkey::new_unique();
let from_account = RefCell::new(
AccountSharedData::new_data(

View File

@ -6,7 +6,10 @@ use crate::{
lamports::LamportsError,
pubkey::Pubkey,
};
use std::cell::{RefCell, RefMut};
use std::{
cell::{RefCell, RefMut},
pin::Pin,
};
pub type TransactionAccount = (Pubkey, AccountSharedData);
@ -23,8 +26,8 @@ pub struct InstructionAccount {
/// This context is valid for the entire duration of a transaction being processed.
#[derive(Debug)]
pub struct TransactionContext {
account_keys: Vec<Pubkey>,
accounts: Vec<RefCell<AccountSharedData>>,
account_keys: Pin<Box<[Pubkey]>>,
accounts: Pin<Box<[RefCell<AccountSharedData>]>>,
instruction_context_capacity: usize,
instruction_context_stack: Vec<InstructionContext>,
return_data: (Pubkey, Vec<u8>),
@ -36,13 +39,14 @@ impl TransactionContext {
transaction_accounts: Vec<TransactionAccount>,
instruction_context_capacity: usize,
) -> Self {
let (account_keys, accounts) = transaction_accounts
.into_iter()
.map(|(key, account)| (key, RefCell::new(account)))
.unzip();
let (account_keys, accounts): (Vec<Pubkey>, Vec<RefCell<AccountSharedData>>) =
transaction_accounts
.into_iter()
.map(|(key, account)| (key, RefCell::new(account)))
.unzip();
Self {
account_keys,
accounts,
account_keys: Pin::new(account_keys.into_boxed_slice()),
accounts: Pin::new(accounts.into_boxed_slice()),
instruction_context_capacity,
instruction_context_stack: Vec::with_capacity(instruction_context_capacity),
return_data: (Pubkey::default(), Vec::new()),
@ -51,10 +55,10 @@ impl TransactionContext {
/// Used by the bank in the runtime to write back the processed accounts
pub fn deconstruct(self) -> Vec<TransactionAccount> {
self.account_keys
Vec::from(Pin::into_inner(self.account_keys))
.into_iter()
.zip(
self.accounts
Vec::from(Pin::into_inner(self.accounts))
.into_iter()
.map(|account| account.into_inner()),
)
@ -66,8 +70,7 @@ impl TransactionContext {
if !self.instruction_context_stack.is_empty() {
return Err(InstructionError::CallDepth);
}
Ok(self
.accounts
Ok(Vec::from(Pin::into_inner(self.accounts))
.into_iter()
.map(|account| account.into_inner())
.collect())
@ -243,21 +246,29 @@ impl InstructionContext {
.map(|index| index.saturating_add(self.program_accounts.len()))
}
/// Translates the given instruction wide index into a transaction wide index
pub fn get_index_in_transaction(
&self,
index_in_instruction: usize,
) -> Result<usize, InstructionError> {
if index_in_instruction < self.program_accounts.len() {
Ok(self.program_accounts[index_in_instruction])
} else if index_in_instruction < self.get_number_of_accounts() {
Ok(self.instruction_accounts
[index_in_instruction.saturating_sub(self.program_accounts.len())]
.index_in_transaction)
} else {
Err(InstructionError::NotEnoughAccountKeys)
}
}
/// Tries to borrow an account from this Instruction
pub fn try_borrow_account<'a, 'b: 'a>(
&'a self,
transaction_context: &'b TransactionContext,
index_in_instruction: usize,
) -> Result<BorrowedAccount<'a>, InstructionError> {
let index_in_transaction = if index_in_instruction < self.program_accounts.len() {
self.program_accounts[index_in_instruction]
} else if index_in_instruction < self.get_number_of_accounts() {
self.instruction_accounts
[index_in_instruction.saturating_sub(self.program_accounts.len())]
.index_in_transaction
} else {
return Err(InstructionError::NotEnoughAccountKeys);
};
let index_in_transaction = self.get_index_in_transaction(index_in_instruction)?;
if index_in_transaction >= transaction_context.accounts.len() {
return Err(InstructionError::MissingAccount);
}
@ -331,12 +342,13 @@ impl<'a> BorrowedAccount<'a> {
}
/// Assignes the owner of this account (transaction wide)
pub fn set_owner(&mut self, pubkey: Pubkey) -> Result<(), InstructionError> {
if !self.is_writable() {
return Err(InstructionError::Immutable);
pub fn set_owner(&mut self, pubkey: &[u8]) -> Result<(), InstructionError> {
self.account.copy_into_owner_from_slice(pubkey);
if self.is_writable() {
Ok(())
} else {
Err(InstructionError::Immutable)
}
self.account.set_owner(pubkey);
Ok(())
}
/// Returns the number of lamports of this account (transaction wide)
@ -346,11 +358,12 @@ impl<'a> BorrowedAccount<'a> {
/// Overwrites the number of lamports of this account (transaction wide)
pub fn set_lamports(&mut self, lamports: u64) -> Result<(), InstructionError> {
if !self.is_writable() {
return Err(InstructionError::Immutable);
}
self.account.set_lamports(lamports);
Ok(())
if self.is_writable() {
Ok(())
} else {
Err(InstructionError::Immutable)
}
}
/// Adds lamports to this account (transaction wide)
@ -376,26 +389,23 @@ impl<'a> BorrowedAccount<'a> {
self.account.data()
}
/// Returns a writable slice of the account data (transaction wide)
pub fn get_data_mut(&mut self) -> Result<&mut [u8], InstructionError> {
if !self.is_writable() {
return Err(InstructionError::Immutable);
/// Overwrites the account data and size (transaction wide)
pub fn set_data(&mut self, data: &[u8]) -> Result<(), InstructionError> {
if data.len() == self.account.data().len() {
self.account.data_as_mut_slice().copy_from_slice(data);
} else {
self.account.set_data_from_slice(data);
}
if self.is_writable() {
Ok(())
} else {
Err(InstructionError::Immutable)
}
Ok(self.account.data_as_mut_slice())
}
/// Guarded alternative to `get_data_mut()?.copy_from_slice()` which checks if the account size matches
pub fn copy_from_slice(&mut self, data: &[u8]) -> Result<(), InstructionError> {
if !self.is_writable() {
return Err(InstructionError::Immutable);
}
let account_data = self.account.data_as_mut_slice();
if data.len() != account_data.len() {
return Err(InstructionError::AccountDataSizeChanged);
}
account_data.copy_from_slice(data);
Ok(())
}
/*pub fn realloc(&self, new_len: usize, zero_init: bool) {
// TODO
}*/
/// Deserializes the account data into a state
pub fn get_state<T: serde::de::DeserializeOwned>(&self) -> Result<T, InstructionError> {
@ -406,22 +416,20 @@ impl<'a> BorrowedAccount<'a> {
/// Serializes a state into the account data
pub fn set_state<T: serde::Serialize>(&mut self, state: &T) -> Result<(), InstructionError> {
if !self.is_writable() {
return Err(InstructionError::Immutable);
}
let data = self.account.data_as_mut_slice();
let serialized_size =
bincode::serialized_size(state).map_err(|_| InstructionError::GenericError)?;
if serialized_size > data.len() as u64 {
return Err(InstructionError::AccountDataTooSmall);
}
bincode::serialize_into(&mut *data, state).map_err(|_| InstructionError::GenericError)
bincode::serialize_into(&mut *data, state).map_err(|_| InstructionError::GenericError)?;
if self.is_writable() {
Ok(())
} else {
Err(InstructionError::Immutable)
}
}
/*pub fn realloc(&self, new_len: usize, zero_init: bool) {
// TODO
}*/
/// Returns whether this account is executable (transaction wide)
pub fn is_executable(&self) -> bool {
self.account.executable()
@ -429,11 +437,17 @@ impl<'a> BorrowedAccount<'a> {
/// Configures whether this account is executable (transaction wide)
pub fn set_executable(&mut self, is_executable: bool) -> Result<(), InstructionError> {
if !self.is_writable() {
return Err(InstructionError::Immutable);
}
self.account.set_executable(is_executable);
Ok(())
if self.is_writable() {
Ok(())
} else {
Err(InstructionError::Immutable)
}
}
/// Returns the rent epoch of this account (transaction wide)
pub fn get_rent_epoch(&self) -> u64 {
self.account.rent_epoch()
}
/// Returns whether this account is a signer (instruction wide)