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(); let mut program_data = Vec::new();
file.read_to_end(&mut program_data) file.read_to_end(&mut program_data)
.map_err(|err| format!("Unable to read program file: {}", err))?; .map_err(|err| format!("Unable to read program file: {}", err))?;
let transaction_context = TransactionContext::new(Vec::new(), 1); let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]); let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
// Verify the program // Verify the program
Executable::<BpfError, ThisInstructionMeter>::from_elf( Executable::<BpfError, ThisInstructionMeter>::from_elf(

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4999,8 +4999,8 @@ mod tests {
#[test] #[test]
fn test_merge() { fn test_merge() {
let transaction_context = TransactionContext::new(Vec::new(), 1); let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
let stake_pubkey = solana_sdk::pubkey::new_rand(); let stake_pubkey = solana_sdk::pubkey::new_rand();
let source_stake_pubkey = solana_sdk::pubkey::new_rand(); let source_stake_pubkey = solana_sdk::pubkey::new_rand();
let authorized_pubkey = solana_sdk::pubkey::new_rand(); let authorized_pubkey = solana_sdk::pubkey::new_rand();
@ -5110,8 +5110,8 @@ mod tests {
#[test] #[test]
fn test_merge_self_fails() { fn test_merge_self_fails() {
let transaction_context = TransactionContext::new(Vec::new(), 1); let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
let stake_address = Pubkey::new_unique(); let stake_address = Pubkey::new_unique();
let authority_pubkey = Pubkey::new_unique(); let authority_pubkey = Pubkey::new_unique();
let signers = HashSet::from_iter(vec![authority_pubkey]); let signers = HashSet::from_iter(vec![authority_pubkey]);
@ -5156,8 +5156,8 @@ mod tests {
#[test] #[test]
fn test_merge_incorrect_authorized_staker() { fn test_merge_incorrect_authorized_staker() {
let transaction_context = TransactionContext::new(Vec::new(), 1); let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
let stake_pubkey = solana_sdk::pubkey::new_rand(); let stake_pubkey = solana_sdk::pubkey::new_rand();
let source_stake_pubkey = solana_sdk::pubkey::new_rand(); let source_stake_pubkey = solana_sdk::pubkey::new_rand();
let authorized_pubkey = solana_sdk::pubkey::new_rand(); let authorized_pubkey = solana_sdk::pubkey::new_rand();
@ -5226,8 +5226,8 @@ mod tests {
#[test] #[test]
fn test_merge_invalid_account_data() { fn test_merge_invalid_account_data() {
let transaction_context = TransactionContext::new(Vec::new(), 1); let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
let stake_pubkey = solana_sdk::pubkey::new_rand(); let stake_pubkey = solana_sdk::pubkey::new_rand();
let source_stake_pubkey = solana_sdk::pubkey::new_rand(); let source_stake_pubkey = solana_sdk::pubkey::new_rand();
let authorized_pubkey = solana_sdk::pubkey::new_rand(); let authorized_pubkey = solana_sdk::pubkey::new_rand();
@ -5277,8 +5277,8 @@ mod tests {
#[test] #[test]
fn test_merge_fake_stake_source() { fn test_merge_fake_stake_source() {
let transaction_context = TransactionContext::new(Vec::new(), 1); let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
let stake_pubkey = solana_sdk::pubkey::new_rand(); let stake_pubkey = solana_sdk::pubkey::new_rand();
let source_stake_pubkey = solana_sdk::pubkey::new_rand(); let source_stake_pubkey = solana_sdk::pubkey::new_rand();
let authorized_pubkey = solana_sdk::pubkey::new_rand(); let authorized_pubkey = solana_sdk::pubkey::new_rand();
@ -5320,8 +5320,8 @@ mod tests {
#[test] #[test]
fn test_merge_active_stake() { fn test_merge_active_stake() {
let transaction_context = TransactionContext::new(Vec::new(), 1); let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
let base_lamports = 4242424242; let base_lamports = 4242424242;
let stake_address = Pubkey::new_unique(); let stake_address = Pubkey::new_unique();
let source_address = Pubkey::new_unique(); let source_address = Pubkey::new_unique();
@ -5943,8 +5943,8 @@ mod tests {
#[test] #[test]
fn test_things_can_merge() { fn test_things_can_merge() {
let transaction_context = TransactionContext::new(Vec::new(), 1); let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
let good_stake = Stake { let good_stake = Stake {
credits_observed: 4242, credits_observed: 4242,
delegation: Delegation { delegation: Delegation {
@ -6042,8 +6042,8 @@ mod tests {
#[test] #[test]
fn test_metas_can_merge_pre_v4() { fn test_metas_can_merge_pre_v4() {
let transaction_context = TransactionContext::new(Vec::new(), 1); let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
// Identical Metas can merge // Identical Metas can merge
assert!(MergeKind::metas_can_merge( assert!(MergeKind::metas_can_merge(
&invoke_context, &invoke_context,
@ -6129,8 +6129,8 @@ mod tests {
#[test] #[test]
fn test_metas_can_merge_v4() { fn test_metas_can_merge_v4() {
let transaction_context = TransactionContext::new(Vec::new(), 1); let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
// Identical Metas can merge // Identical Metas can merge
assert!(MergeKind::metas_can_merge( assert!(MergeKind::metas_can_merge(
&invoke_context, &invoke_context,
@ -6276,8 +6276,8 @@ mod tests {
#[test] #[test]
fn test_merge_kind_get_if_mergeable() { fn test_merge_kind_get_if_mergeable() {
let transaction_context = TransactionContext::new(Vec::new(), 1); let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
let authority_pubkey = Pubkey::new_unique(); let authority_pubkey = Pubkey::new_unique();
let initial_lamports = 4242424242; let initial_lamports = 4242424242;
let rent = Rent::default(); let rent = Rent::default();
@ -6509,8 +6509,8 @@ mod tests {
#[test] #[test]
fn test_merge_kind_merge() { fn test_merge_kind_merge() {
let transaction_context = TransactionContext::new(Vec::new(), 1); let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
let lamports = 424242; let lamports = 424242;
let meta = Meta { let meta = Meta {
rent_exempt_reserve: 42, rent_exempt_reserve: 42,
@ -6588,8 +6588,8 @@ mod tests {
#[test] #[test]
fn test_active_stake_merge() { fn test_active_stake_merge() {
let transaction_context = TransactionContext::new(Vec::new(), 1); let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
let delegation_a = 4_242_424_242u64; let delegation_a = 4_242_424_242u64;
let delegation_b = 6_200_000_000u64; let delegation_b = 6_200_000_000u64;
let credits_a = 124_521_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 program_indices = [0, 1];
let preparation = let preparation =
prepare_mock_invoke_context(transaction_accounts, instruction_accounts, &program_indices); prepare_mock_invoke_context(transaction_accounts, instruction_accounts, &program_indices);
let transaction_context = TransactionContext::new(preparation.transaction_accounts, 1); let mut transaction_context = TransactionContext::new(preparation.transaction_accounts, 1);
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]); let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
invoke_context invoke_context
.push(&preparation.instruction_accounts, &program_indices) .push(
&preparation.instruction_accounts,
&program_indices,
&instruction_data,
)
.unwrap(); .unwrap();
let keyed_accounts = invoke_context.get_keyed_accounts().unwrap();
let (mut parameter_bytes, account_lengths) = serialize_parameters( let (mut parameter_bytes, account_lengths) = serialize_parameters(
keyed_accounts[0].unsigned_key(), invoke_context.transaction_context,
keyed_accounts[1].unsigned_key(), invoke_context
&keyed_accounts[2..], .transaction_context
&instruction_data, .get_current_instruction_context()
.unwrap(),
) )
.unwrap(); .unwrap();
let compute_meter = invoke_context.get_compute_meter(); let compute_meter = invoke_context.get_compute_meter();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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