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:
parent
672fed04cb
commit
73e6038986
|
@ -1991,8 +1991,8 @@ fn read_and_verify_elf(program_location: &str) -> Result<Vec<u8>, Box<dyn std::e
|
|||
let mut program_data = Vec::new();
|
||||
file.read_to_end(&mut program_data)
|
||||
.map_err(|err| format!("Unable to read program file: {}", err))?;
|
||||
let transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
|
||||
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
|
||||
// Verify the program
|
||||
Executable::<BpfError, ThisInstructionMeter>::from_elf(
|
||||
|
|
|
@ -6,7 +6,6 @@ use {
|
|||
},
|
||||
solana_sdk::{
|
||||
account::{AccountSharedData, ReadableAccount},
|
||||
account_utils::StateMut,
|
||||
bpf_loader_upgradeable::{self, UpgradeableLoaderState},
|
||||
compute_budget::ComputeBudget,
|
||||
feature_set::{
|
||||
|
@ -15,7 +14,7 @@ use {
|
|||
},
|
||||
hash::Hash,
|
||||
instruction::{AccountMeta, CompiledInstruction, Instruction, InstructionError},
|
||||
keyed_account::{create_keyed_accounts_unified, keyed_account_at_index, KeyedAccount},
|
||||
keyed_account::{create_keyed_accounts_unified, KeyedAccount},
|
||||
native_loader,
|
||||
pubkey::Pubkey,
|
||||
rent::Rent,
|
||||
|
@ -135,7 +134,7 @@ impl<'a> StackFrame<'a> {
|
|||
}
|
||||
|
||||
pub struct InvokeContext<'a> {
|
||||
pub transaction_context: &'a TransactionContext,
|
||||
pub transaction_context: &'a mut TransactionContext,
|
||||
pub return_data: (Pubkey, Vec<u8>),
|
||||
invoke_stack: Vec<StackFrame<'a>>,
|
||||
rent: Rent,
|
||||
|
@ -158,7 +157,7 @@ pub struct InvokeContext<'a> {
|
|||
impl<'a> InvokeContext<'a> {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
transaction_context: &'a TransactionContext,
|
||||
transaction_context: &'a mut TransactionContext,
|
||||
rent: Rent,
|
||||
builtin_programs: &'a [BuiltinProgram],
|
||||
sysvars: &'a [(Pubkey, Vec<u8>)],
|
||||
|
@ -194,7 +193,7 @@ impl<'a> InvokeContext<'a> {
|
|||
}
|
||||
|
||||
pub fn new_mock(
|
||||
transaction_context: &'a TransactionContext,
|
||||
transaction_context: &'a mut TransactionContext,
|
||||
builtin_programs: &'a [BuiltinProgram],
|
||||
) -> Self {
|
||||
Self::new(
|
||||
|
@ -218,8 +217,13 @@ impl<'a> InvokeContext<'a> {
|
|||
&mut self,
|
||||
instruction_accounts: &[InstructionAccount],
|
||||
program_indices: &[usize],
|
||||
instruction_data: &[u8],
|
||||
) -> Result<(), InstructionError> {
|
||||
if self.invoke_stack.len() > self.compute_budget.max_invoke_depth {
|
||||
if self
|
||||
.transaction_context
|
||||
.get_instruction_context_stack_height()
|
||||
> self.compute_budget.max_invoke_depth
|
||||
{
|
||||
return Err(InstructionError::CallDepth);
|
||||
}
|
||||
|
||||
|
@ -234,7 +238,11 @@ impl<'a> InvokeContext<'a> {
|
|||
{
|
||||
return Err(InstructionError::UnsupportedProgramId);
|
||||
}
|
||||
if self.invoke_stack.is_empty() {
|
||||
if self
|
||||
.transaction_context
|
||||
.get_instruction_context_stack_height()
|
||||
== 0
|
||||
{
|
||||
let mut compute_budget = self.compute_budget;
|
||||
if !self.feature_set.is_active(&tx_wide_compute_cap::id())
|
||||
&& self.feature_set.is_active(&neon_evm_compute_budget::id())
|
||||
|
@ -278,15 +286,26 @@ impl<'a> InvokeContext<'a> {
|
|||
};
|
||||
visit_each_account_once(instruction_accounts, &mut work)?;
|
||||
} else {
|
||||
let contains = self
|
||||
.invoke_stack
|
||||
.iter()
|
||||
.any(|frame| frame.program_id() == program_id);
|
||||
let is_last = if let Some(last_frame) = self.invoke_stack.last() {
|
||||
last_frame.program_id() == program_id
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let contains = (0..self
|
||||
.transaction_context
|
||||
.get_instruction_context_stack_height())
|
||||
.any(|level| {
|
||||
self.transaction_context
|
||||
.get_instruction_context_at(level)
|
||||
.and_then(|instruction_context| {
|
||||
instruction_context.try_borrow_program_account(self.transaction_context)
|
||||
})
|
||||
.map(|program_account| Some(program_account.get_key()) == program_id)
|
||||
.unwrap_or_else(|_| program_id.is_none())
|
||||
});
|
||||
let is_last = self
|
||||
.transaction_context
|
||||
.get_current_instruction_context()
|
||||
.and_then(|instruction_context| {
|
||||
instruction_context.try_borrow_program_account(self.transaction_context)
|
||||
})
|
||||
.map(|program_account| Some(program_account.get_key()) == program_id)
|
||||
.unwrap_or_else(|_| program_id.is_none());
|
||||
if contains && !is_last {
|
||||
// Reentrancy not allowed unless caller is calling itself
|
||||
return Err(InstructionError::ReentrancyNotAllowed);
|
||||
|
@ -318,21 +337,27 @@ impl<'a> InvokeContext<'a> {
|
|||
}))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Unsafe will be removed together with the keyed_accounts
|
||||
self.invoke_stack.push(StackFrame::new(
|
||||
program_indices.len(),
|
||||
create_keyed_accounts_unified(keyed_accounts.as_slice()),
|
||||
create_keyed_accounts_unified(unsafe {
|
||||
std::mem::transmute(keyed_accounts.as_slice())
|
||||
}),
|
||||
));
|
||||
Ok(())
|
||||
self.transaction_context
|
||||
.push(program_indices, instruction_accounts, instruction_data)
|
||||
}
|
||||
|
||||
/// Pop a stack frame from the invocation stack
|
||||
pub fn pop(&mut self) {
|
||||
pub fn pop(&mut self) -> Result<(), InstructionError> {
|
||||
self.invoke_stack.pop();
|
||||
self.transaction_context.pop()
|
||||
}
|
||||
|
||||
/// Current depth of the invocation stack
|
||||
pub fn invoke_depth(&self) -> usize {
|
||||
self.invoke_stack.len()
|
||||
self.transaction_context
|
||||
.get_instruction_context_stack_height()
|
||||
}
|
||||
|
||||
/// Verify the results of an instruction
|
||||
|
@ -341,12 +366,11 @@ impl<'a> InvokeContext<'a> {
|
|||
instruction_accounts: &[InstructionAccount],
|
||||
program_indices: &[usize],
|
||||
) -> Result<(), InstructionError> {
|
||||
let program_id = self
|
||||
.invoke_stack
|
||||
.last()
|
||||
.and_then(|frame| frame.program_id())
|
||||
.ok_or(InstructionError::CallDepth)?;
|
||||
let do_support_realloc = self.feature_set.is_active(&do_support_realloc::id());
|
||||
let program_id = self
|
||||
.transaction_context
|
||||
.get_program_key()
|
||||
.map_err(|_| InstructionError::CallDepth)?;
|
||||
|
||||
// Verify all executable accounts have zero outstanding refs
|
||||
for account_index in program_indices.iter() {
|
||||
|
@ -414,23 +438,18 @@ impl<'a> InvokeContext<'a> {
|
|||
fn verify_and_update(
|
||||
&mut self,
|
||||
instruction_accounts: &[InstructionAccount],
|
||||
caller_write_privileges: Option<&[bool]>,
|
||||
before_instruction_context_push: bool,
|
||||
) -> Result<(), InstructionError> {
|
||||
let do_support_realloc = self.feature_set.is_active(&do_support_realloc::id());
|
||||
let program_id = self
|
||||
.invoke_stack
|
||||
.last()
|
||||
.and_then(|frame| frame.program_id())
|
||||
.ok_or(InstructionError::CallDepth)?;
|
||||
let rent = &self.rent;
|
||||
let log_collector = &self.log_collector;
|
||||
let transaction_context = self.transaction_context;
|
||||
let pre_accounts = &mut self.pre_accounts;
|
||||
let timings = &mut self.timings;
|
||||
let transaction_context = &self.transaction_context;
|
||||
let instruction_context = transaction_context.get_current_instruction_context()?;
|
||||
let program_id = transaction_context
|
||||
.get_program_key()
|
||||
.map_err(|_| InstructionError::CallDepth)?;
|
||||
|
||||
// Verify the per-account instruction results
|
||||
let (mut pre_sum, mut post_sum) = (0_u128, 0_u128);
|
||||
let mut work = |index_in_instruction: usize, instruction_account: &InstructionAccount| {
|
||||
let mut work = |_index_in_instruction: usize, instruction_account: &InstructionAccount| {
|
||||
if instruction_account.index_in_transaction
|
||||
< transaction_context.get_number_of_accounts()
|
||||
{
|
||||
|
@ -438,13 +457,18 @@ impl<'a> InvokeContext<'a> {
|
|||
.get_key_of_account_at_index(instruction_account.index_in_transaction);
|
||||
let account = transaction_context
|
||||
.get_account_at_index(instruction_account.index_in_transaction);
|
||||
let is_writable = if let Some(caller_write_privileges) = caller_write_privileges {
|
||||
caller_write_privileges[index_in_instruction]
|
||||
let is_writable = if before_instruction_context_push {
|
||||
instruction_context
|
||||
.try_borrow_account(
|
||||
self.transaction_context,
|
||||
instruction_account.index_in_caller,
|
||||
)?
|
||||
.is_writable()
|
||||
} else {
|
||||
instruction_account.is_writable
|
||||
};
|
||||
// Find the matching PreAccount
|
||||
for pre_account in pre_accounts.iter_mut() {
|
||||
for pre_account in self.pre_accounts.iter_mut() {
|
||||
if key == pre_account.key() {
|
||||
{
|
||||
// Verify account has no outstanding references
|
||||
|
@ -457,15 +481,15 @@ impl<'a> InvokeContext<'a> {
|
|||
.verify(
|
||||
program_id,
|
||||
is_writable,
|
||||
rent,
|
||||
&self.rent,
|
||||
&account,
|
||||
timings,
|
||||
&mut self.timings,
|
||||
false,
|
||||
do_support_realloc,
|
||||
)
|
||||
.map_err(|err| {
|
||||
ic_logger_msg!(
|
||||
log_collector,
|
||||
self.log_collector,
|
||||
"failed to verify account {}: {}",
|
||||
key,
|
||||
err
|
||||
|
@ -502,7 +526,7 @@ impl<'a> InvokeContext<'a> {
|
|||
instruction: Instruction,
|
||||
signers: &[Pubkey],
|
||||
) -> Result<(), InstructionError> {
|
||||
let (instruction_accounts, caller_write_privileges, program_indices) =
|
||||
let (instruction_accounts, program_indices) =
|
||||
self.prepare_instruction(&instruction, signers)?;
|
||||
let mut prev_account_sizes = Vec::with_capacity(instruction_accounts.len());
|
||||
for instruction_account in instruction_accounts.iter() {
|
||||
|
@ -519,7 +543,6 @@ impl<'a> InvokeContext<'a> {
|
|||
self.process_instruction(
|
||||
&instruction.data,
|
||||
&instruction_accounts,
|
||||
Some(&caller_write_privileges),
|
||||
&program_indices,
|
||||
&mut compute_units_consumed,
|
||||
)?;
|
||||
|
@ -556,11 +579,11 @@ impl<'a> InvokeContext<'a> {
|
|||
&mut self,
|
||||
instruction: &Instruction,
|
||||
signers: &[Pubkey],
|
||||
) -> Result<(Vec<InstructionAccount>, Vec<bool>, Vec<usize>), InstructionError> {
|
||||
) -> Result<(Vec<InstructionAccount>, Vec<usize>), InstructionError> {
|
||||
// Finds the index of each account in the instruction by its pubkey.
|
||||
// Then normalizes / unifies the privileges of duplicate accounts.
|
||||
// Note: This works like visit_each_account_once() and is an O(n^2) algorithm too.
|
||||
let caller_keyed_accounts = self.get_keyed_accounts()?;
|
||||
let instruction_context = self.transaction_context.get_current_instruction_context()?;
|
||||
let mut deduplicated_instruction_accounts: Vec<InstructionAccount> = Vec::new();
|
||||
let mut duplicate_indicies = Vec::with_capacity(instruction.accounts.len());
|
||||
for account_meta in instruction.accounts.iter() {
|
||||
|
@ -587,9 +610,8 @@ impl<'a> InvokeContext<'a> {
|
|||
instruction_account.is_signer |= account_meta.is_signer;
|
||||
instruction_account.is_writable |= account_meta.is_writable;
|
||||
} else {
|
||||
let index_in_caller = caller_keyed_accounts
|
||||
.iter()
|
||||
.position(|keyed_account| *keyed_account.unsigned_key() == account_meta.pubkey)
|
||||
let index_in_caller = instruction_context
|
||||
.find_index_of_account(self.transaction_context, &account_meta.pubkey)
|
||||
.ok_or_else(|| {
|
||||
ic_msg!(
|
||||
self,
|
||||
|
@ -598,6 +620,33 @@ impl<'a> InvokeContext<'a> {
|
|||
);
|
||||
InstructionError::MissingAccount
|
||||
})?;
|
||||
let borrowed_account = instruction_context
|
||||
.try_borrow_account(self.transaction_context, index_in_caller)?;
|
||||
|
||||
// Readonly in caller cannot become writable in callee
|
||||
if account_meta.is_writable && !borrowed_account.is_writable() {
|
||||
ic_msg!(
|
||||
self,
|
||||
"{}'s writable privilege escalated",
|
||||
borrowed_account.get_key(),
|
||||
);
|
||||
return Err(InstructionError::PrivilegeEscalation);
|
||||
}
|
||||
|
||||
// To be signed in the callee,
|
||||
// it must be either signed in the caller or by the program
|
||||
if account_meta.is_signer
|
||||
&& !(borrowed_account.is_signer()
|
||||
|| signers.contains(borrowed_account.get_key()))
|
||||
{
|
||||
ic_msg!(
|
||||
self,
|
||||
"{}'s signer privilege escalated",
|
||||
borrowed_account.get_key()
|
||||
);
|
||||
return Err(InstructionError::PrivilegeEscalation);
|
||||
}
|
||||
|
||||
duplicate_indicies.push(deduplicated_instruction_accounts.len());
|
||||
deduplicated_instruction_accounts.push(InstructionAccount {
|
||||
index_in_transaction,
|
||||
|
@ -612,66 +661,25 @@ impl<'a> InvokeContext<'a> {
|
|||
.map(|duplicate_index| deduplicated_instruction_accounts[duplicate_index].clone())
|
||||
.collect();
|
||||
|
||||
// Check for privilege escalation
|
||||
let caller_write_privileges = instruction_accounts
|
||||
.iter()
|
||||
.map(|instruction_account| {
|
||||
let keyed_account = &caller_keyed_accounts[instruction_account.index_in_caller];
|
||||
|
||||
// Readonly in caller cannot become writable in callee
|
||||
if instruction_account.is_writable && !keyed_account.is_writable() {
|
||||
ic_msg!(
|
||||
self,
|
||||
"{}'s writable privilege escalated",
|
||||
keyed_account.unsigned_key(),
|
||||
);
|
||||
return Err(InstructionError::PrivilegeEscalation);
|
||||
}
|
||||
|
||||
// To be signed in the callee,
|
||||
// it must be either signed in the caller or by the program
|
||||
if instruction_account.is_signer
|
||||
&& !(keyed_account.signer_key().is_some()
|
||||
|| signers.contains(keyed_account.unsigned_key()))
|
||||
{
|
||||
ic_msg!(
|
||||
self,
|
||||
"{}'s signer privilege escalated",
|
||||
keyed_account.unsigned_key()
|
||||
);
|
||||
return Err(InstructionError::PrivilegeEscalation);
|
||||
}
|
||||
|
||||
Ok(keyed_account.is_writable())
|
||||
})
|
||||
.collect::<Result<Vec<bool>, InstructionError>>()?;
|
||||
|
||||
// Find and validate executables / program accounts
|
||||
let callee_program_id = instruction.program_id;
|
||||
let program_account_index = caller_keyed_accounts
|
||||
.iter()
|
||||
.find(|keyed_account| &callee_program_id == keyed_account.unsigned_key())
|
||||
.and_then(|_keyed_account| {
|
||||
self.transaction_context
|
||||
.find_index_of_program_account(&callee_program_id)
|
||||
})
|
||||
let program_account_index = instruction_context
|
||||
.find_index_of_account(self.transaction_context, &callee_program_id)
|
||||
.ok_or_else(|| {
|
||||
ic_msg!(self, "Unknown program {}", callee_program_id);
|
||||
InstructionError::MissingAccount
|
||||
})?;
|
||||
let program_account = self
|
||||
.transaction_context
|
||||
.get_account_at_index(program_account_index)
|
||||
.borrow();
|
||||
if !program_account.executable() {
|
||||
let borrowed_program_account = instruction_context
|
||||
.try_borrow_account(self.transaction_context, program_account_index)?;
|
||||
if !borrowed_program_account.is_executable() {
|
||||
ic_msg!(self, "Account {} is not executable", callee_program_id);
|
||||
return Err(InstructionError::AccountNotExecutable);
|
||||
}
|
||||
let mut program_indices = vec![];
|
||||
if program_account.owner() == &bpf_loader_upgradeable::id() {
|
||||
if borrowed_program_account.get_owner() == &bpf_loader_upgradeable::id() {
|
||||
if let UpgradeableLoaderState::Program {
|
||||
programdata_address,
|
||||
} = program_account.state()?
|
||||
} = borrowed_program_account.get_state()?
|
||||
{
|
||||
if let Some(programdata_account_index) = self
|
||||
.transaction_context
|
||||
|
@ -695,21 +703,16 @@ impl<'a> InvokeContext<'a> {
|
|||
return Err(InstructionError::MissingAccount);
|
||||
}
|
||||
}
|
||||
program_indices.push(program_account_index);
|
||||
program_indices.push(borrowed_program_account.get_index_in_transaction());
|
||||
|
||||
Ok((
|
||||
instruction_accounts,
|
||||
caller_write_privileges,
|
||||
program_indices,
|
||||
))
|
||||
Ok((instruction_accounts, program_indices))
|
||||
}
|
||||
|
||||
/// Processes a cross-program instruction and returns how many compute units were used
|
||||
/// Processes an instruction and returns how many compute units were used
|
||||
pub fn process_instruction(
|
||||
&mut self,
|
||||
instruction_data: &[u8],
|
||||
instruction_accounts: &[InstructionAccount],
|
||||
caller_write_privileges: Option<&[bool]>,
|
||||
program_indices: &[usize],
|
||||
compute_units_consumed: &mut u64,
|
||||
) -> Result<(), InstructionError> {
|
||||
|
@ -719,14 +722,17 @@ impl<'a> InvokeContext<'a> {
|
|||
.map(|index| *self.transaction_context.get_key_of_account_at_index(*index))
|
||||
.unwrap_or_else(native_loader::id);
|
||||
|
||||
let is_lowest_invocation_level = self.invoke_stack.is_empty();
|
||||
let is_lowest_invocation_level = self
|
||||
.transaction_context
|
||||
.get_instruction_context_stack_height()
|
||||
== 0;
|
||||
if is_lowest_invocation_level {
|
||||
if let Some(instruction_recorder) = &self.instruction_recorder {
|
||||
instruction_recorder.borrow_mut().begin_next_recording();
|
||||
}
|
||||
} else {
|
||||
// Verify the calling program hasn't misbehaved
|
||||
self.verify_and_update(instruction_accounts, caller_write_privileges)?;
|
||||
self.verify_and_update(instruction_accounts, true)?;
|
||||
|
||||
// Record instruction
|
||||
if let Some(instruction_recorder) = &self.instruction_recorder {
|
||||
|
@ -748,7 +754,7 @@ impl<'a> InvokeContext<'a> {
|
|||
}
|
||||
|
||||
let result = self
|
||||
.push(instruction_accounts, program_indices)
|
||||
.push(instruction_accounts, program_indices, instruction_data)
|
||||
.and_then(|_| {
|
||||
self.return_data = (program_id, Vec::new());
|
||||
let pre_remaining_units = self.compute_meter.borrow().get_remaining();
|
||||
|
@ -761,12 +767,12 @@ impl<'a> InvokeContext<'a> {
|
|||
if is_lowest_invocation_level {
|
||||
self.verify(instruction_accounts, program_indices)
|
||||
} else {
|
||||
self.verify_and_update(instruction_accounts, None)
|
||||
self.verify_and_update(instruction_accounts, false)
|
||||
}
|
||||
});
|
||||
|
||||
// Pop the invoke_stack to restore previous state
|
||||
self.pop();
|
||||
let _ = self.pop();
|
||||
result
|
||||
}
|
||||
|
||||
|
@ -775,14 +781,16 @@ impl<'a> InvokeContext<'a> {
|
|||
&mut self,
|
||||
instruction_data: &[u8],
|
||||
) -> Result<(), InstructionError> {
|
||||
let keyed_accounts = self.get_keyed_accounts()?;
|
||||
let root_account = keyed_account_at_index(keyed_accounts, 0)
|
||||
let instruction_context = self.transaction_context.get_current_instruction_context()?;
|
||||
let borrowed_root_account = instruction_context
|
||||
.try_borrow_account(self.transaction_context, 0)
|
||||
.map_err(|_| InstructionError::UnsupportedProgramId)?;
|
||||
let root_id = root_account.unsigned_key();
|
||||
let owner_id = &root_account.owner()?;
|
||||
let root_id = borrowed_root_account.get_key();
|
||||
let owner_id = borrowed_root_account.get_owner();
|
||||
if solana_sdk::native_loader::check_id(owner_id) {
|
||||
for entry in self.builtin_programs {
|
||||
if entry.program_id == *root_id {
|
||||
drop(borrowed_root_account);
|
||||
// Call the builtin program
|
||||
return (entry.process_instruction)(
|
||||
1, // root_id to be skipped
|
||||
|
@ -792,6 +800,7 @@ impl<'a> InvokeContext<'a> {
|
|||
}
|
||||
}
|
||||
if !self.feature_set.is_active(&remove_native_loader::id()) {
|
||||
drop(borrowed_root_account);
|
||||
let native_loader = NativeLoader::default();
|
||||
// Call the program via the native loader
|
||||
return native_loader.process_instruction(0, instruction_data, self);
|
||||
|
@ -799,6 +808,7 @@ impl<'a> InvokeContext<'a> {
|
|||
} else {
|
||||
for entry in self.builtin_programs {
|
||||
if entry.program_id == *owner_id {
|
||||
drop(borrowed_root_account);
|
||||
// Call the program via a builtin loader
|
||||
return (entry.process_instruction)(
|
||||
0, // no root_id was provided
|
||||
|
@ -811,27 +821,6 @@ impl<'a> InvokeContext<'a> {
|
|||
Err(InstructionError::UnsupportedProgramId)
|
||||
}
|
||||
|
||||
/// Get the program ID of the currently executing program
|
||||
pub fn get_caller(&self) -> Result<&Pubkey, InstructionError> {
|
||||
self.invoke_stack
|
||||
.last()
|
||||
.and_then(|frame| frame.program_id())
|
||||
.ok_or(InstructionError::CallDepth)
|
||||
}
|
||||
|
||||
/// Get the owner of the currently executing program
|
||||
pub fn get_loader(&self) -> Result<Pubkey, InstructionError> {
|
||||
let frame = self
|
||||
.invoke_stack
|
||||
.last()
|
||||
.ok_or(InstructionError::CallDepth)?;
|
||||
let first_instruction_account = frame
|
||||
.number_of_program_accounts
|
||||
.checked_sub(1)
|
||||
.ok_or(InstructionError::CallDepth)?;
|
||||
frame.keyed_accounts[first_instruction_account].owner()
|
||||
}
|
||||
|
||||
/// Removes the first keyed account
|
||||
#[deprecated(
|
||||
since = "1.9.0",
|
||||
|
@ -857,17 +846,6 @@ impl<'a> InvokeContext<'a> {
|
|||
.ok_or(InstructionError::CallDepth)
|
||||
}
|
||||
|
||||
/// Get the list of keyed accounts without the chain of program accounts
|
||||
///
|
||||
/// Note: This only contains the `KeyedAccount`s passed by the caller.
|
||||
pub fn get_instruction_keyed_accounts(&self) -> Result<&[KeyedAccount], InstructionError> {
|
||||
let frame = self
|
||||
.invoke_stack
|
||||
.last()
|
||||
.ok_or(InstructionError::CallDepth)?;
|
||||
Ok(&frame.keyed_accounts[frame.number_of_program_accounts..])
|
||||
}
|
||||
|
||||
/// Get this invocation's LogCollector
|
||||
pub fn get_log_collector(&self) -> Option<Rc<RefCell<LogCollector>>> {
|
||||
self.log_collector.clone()
|
||||
|
@ -975,13 +953,13 @@ pub fn with_mock_invoke_context<R, F: FnMut(&mut InvokeContext) -> R>(
|
|||
}];
|
||||
let preparation =
|
||||
prepare_mock_invoke_context(transaction_accounts, instruction_accounts, &program_indices);
|
||||
let transaction_context = TransactionContext::new(
|
||||
let mut transaction_context = TransactionContext::new(
|
||||
preparation.transaction_accounts,
|
||||
ComputeBudget::default().max_invoke_depth,
|
||||
ComputeBudget::default().max_invoke_depth.saturating_add(1),
|
||||
);
|
||||
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
|
||||
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
invoke_context
|
||||
.push(&preparation.instruction_accounts, &program_indices)
|
||||
.push(&preparation.instruction_accounts, &program_indices, &[])
|
||||
.unwrap();
|
||||
callback(&mut invoke_context)
|
||||
}
|
||||
|
@ -1003,15 +981,20 @@ pub fn mock_process_instruction_with_sysvars(
|
|||
preparation
|
||||
.transaction_accounts
|
||||
.push((*loader_id, processor_account));
|
||||
let transaction_context = TransactionContext::new(
|
||||
let mut transaction_context = TransactionContext::new(
|
||||
preparation.transaction_accounts,
|
||||
ComputeBudget::default().max_invoke_depth,
|
||||
ComputeBudget::default().max_invoke_depth.saturating_add(1),
|
||||
);
|
||||
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
|
||||
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
invoke_context.sysvars = sysvars;
|
||||
let result = invoke_context
|
||||
.push(&preparation.instruction_accounts, &program_indices)
|
||||
.push(
|
||||
&preparation.instruction_accounts,
|
||||
&program_indices,
|
||||
instruction_data,
|
||||
)
|
||||
.and_then(|_| process_instruction(1, instruction_data, &mut invoke_context));
|
||||
invoke_context.pop().unwrap();
|
||||
assert_eq!(result, expected_result);
|
||||
let mut transaction_accounts = transaction_context.deconstruct_without_keys().unwrap();
|
||||
transaction_accounts.pop();
|
||||
|
@ -1062,7 +1045,10 @@ mod tests {
|
|||
use {
|
||||
super::*,
|
||||
serde::{Deserialize, Serialize},
|
||||
solana_sdk::account::{ReadableAccount, WritableAccount},
|
||||
solana_sdk::{
|
||||
account::{ReadableAccount, WritableAccount},
|
||||
keyed_account::keyed_account_at_index,
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
|
@ -1163,7 +1149,8 @@ mod tests {
|
|||
data: &[u8],
|
||||
invoke_context: &mut InvokeContext,
|
||||
) -> Result<(), InstructionError> {
|
||||
let program_id = invoke_context.get_caller()?;
|
||||
let transaction_context = &invoke_context.transaction_context;
|
||||
let program_id = transaction_context.get_program_key()?;
|
||||
let keyed_accounts = invoke_context.get_keyed_accounts()?;
|
||||
assert_eq!(
|
||||
*program_id,
|
||||
|
@ -1242,14 +1229,14 @@ mod tests {
|
|||
is_writable: false,
|
||||
});
|
||||
}
|
||||
let transaction_context = TransactionContext::new(accounts, MAX_DEPTH);
|
||||
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
|
||||
let mut transaction_context = TransactionContext::new(accounts, MAX_DEPTH);
|
||||
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
|
||||
// Check call depth increases and has a limit
|
||||
let mut depth_reached = 0;
|
||||
for _ in 0..invoke_stack.len() {
|
||||
if Err(InstructionError::CallDepth)
|
||||
== invoke_context.push(&instruction_accounts, &[MAX_DEPTH + depth_reached])
|
||||
== invoke_context.push(&instruction_accounts, &[MAX_DEPTH + depth_reached], &[])
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
@ -1277,12 +1264,13 @@ mod tests {
|
|||
];
|
||||
|
||||
// modify account owned by the program
|
||||
transaction_context
|
||||
invoke_context
|
||||
.transaction_context
|
||||
.get_account_at_index(owned_index)
|
||||
.borrow_mut()
|
||||
.data_as_mut_slice()[0] = (MAX_DEPTH + owned_index) as u8;
|
||||
invoke_context
|
||||
.verify_and_update(&instruction_accounts, None)
|
||||
.verify_and_update(&instruction_accounts, false)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
invoke_context.pre_accounts[owned_index].data()[0],
|
||||
|
@ -1290,25 +1278,28 @@ mod tests {
|
|||
);
|
||||
|
||||
// modify account not owned by the program
|
||||
let data = transaction_context
|
||||
let data = invoke_context
|
||||
.transaction_context
|
||||
.get_account_at_index(not_owned_index)
|
||||
.borrow_mut()
|
||||
.data()[0];
|
||||
transaction_context
|
||||
invoke_context
|
||||
.transaction_context
|
||||
.get_account_at_index(not_owned_index)
|
||||
.borrow_mut()
|
||||
.data_as_mut_slice()[0] = (MAX_DEPTH + not_owned_index) as u8;
|
||||
assert_eq!(
|
||||
invoke_context.verify_and_update(&instruction_accounts, None),
|
||||
invoke_context.verify_and_update(&instruction_accounts, false),
|
||||
Err(InstructionError::ExternalAccountDataModified)
|
||||
);
|
||||
assert_eq!(invoke_context.pre_accounts[not_owned_index].data()[0], data);
|
||||
transaction_context
|
||||
invoke_context
|
||||
.transaction_context
|
||||
.get_account_at_index(not_owned_index)
|
||||
.borrow_mut()
|
||||
.data_as_mut_slice()[0] = data;
|
||||
|
||||
invoke_context.pop();
|
||||
invoke_context.pop().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1317,20 +1308,14 @@ mod tests {
|
|||
let accounts = vec![(solana_sdk::pubkey::new_rand(), AccountSharedData::default())];
|
||||
let instruction_accounts = vec![];
|
||||
let program_indices = vec![0];
|
||||
let transaction_context = TransactionContext::new(accounts, 1);
|
||||
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
|
||||
let mut transaction_context = TransactionContext::new(accounts, 1);
|
||||
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
invoke_context
|
||||
.push(&instruction_accounts, &program_indices)
|
||||
.push(&instruction_accounts, &program_indices, &[])
|
||||
.unwrap();
|
||||
assert!(invoke_context
|
||||
.verify(&instruction_accounts, &program_indices)
|
||||
.is_ok());
|
||||
|
||||
let mut _borrowed = transaction_context.get_account_at_index(0).borrow();
|
||||
assert_eq!(
|
||||
invoke_context.verify(&instruction_accounts, &program_indices),
|
||||
Err(InstructionError::AccountBorrowOutstanding)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1367,12 +1352,15 @@ mod tests {
|
|||
is_writable: index_in_transaction < 2,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let transaction_context = TransactionContext::new(accounts, 2);
|
||||
let mut invoke_context = InvokeContext::new_mock(&transaction_context, builtin_programs);
|
||||
let mut transaction_context = TransactionContext::new(accounts, 2);
|
||||
let mut invoke_context =
|
||||
InvokeContext::new_mock(&mut transaction_context, builtin_programs);
|
||||
|
||||
// External modification tests
|
||||
{
|
||||
invoke_context.push(&instruction_accounts, &[4]).unwrap();
|
||||
invoke_context
|
||||
.push(&instruction_accounts, &[4], &[])
|
||||
.unwrap();
|
||||
let inner_instruction = Instruction::new_with_bincode(
|
||||
callee_program_id,
|
||||
&MockInstruction::NoopSuccess,
|
||||
|
@ -1411,7 +1399,7 @@ mod tests {
|
|||
.borrow_mut()
|
||||
.data_as_mut_slice()[0] = 0;
|
||||
|
||||
invoke_context.pop();
|
||||
invoke_context.pop().unwrap();
|
||||
}
|
||||
|
||||
// Internal modification tests
|
||||
|
@ -1432,18 +1420,22 @@ mod tests {
|
|||
),
|
||||
];
|
||||
for case in cases {
|
||||
invoke_context.push(&instruction_accounts, &[4]).unwrap();
|
||||
invoke_context
|
||||
.push(&instruction_accounts, &[4], &[])
|
||||
.unwrap();
|
||||
let inner_instruction =
|
||||
Instruction::new_with_bincode(callee_program_id, &case.0, metas.clone());
|
||||
assert_eq!(invoke_context.native_invoke(inner_instruction, &[]), case.1);
|
||||
invoke_context.pop();
|
||||
invoke_context.pop().unwrap();
|
||||
}
|
||||
|
||||
// Compute unit consumption tests
|
||||
let compute_units_to_consume = 10;
|
||||
let expected_results = vec![Ok(()), Err(InstructionError::GenericError)];
|
||||
for expected_result in expected_results {
|
||||
invoke_context.push(&instruction_accounts, &[4]).unwrap();
|
||||
invoke_context
|
||||
.push(&instruction_accounts, &[4], &[])
|
||||
.unwrap();
|
||||
let inner_instruction = Instruction::new_with_bincode(
|
||||
callee_program_id,
|
||||
&MockInstruction::ConsumeComputeUnits {
|
||||
|
@ -1452,16 +1444,14 @@ mod tests {
|
|||
},
|
||||
metas.clone(),
|
||||
);
|
||||
let (inner_instruction_accounts, caller_write_privileges, program_indices) =
|
||||
invoke_context
|
||||
.prepare_instruction(&inner_instruction, &[])
|
||||
.unwrap();
|
||||
let (inner_instruction_accounts, program_indices) = invoke_context
|
||||
.prepare_instruction(&inner_instruction, &[])
|
||||
.unwrap();
|
||||
|
||||
let mut compute_units_consumed = 0;
|
||||
let result = invoke_context.process_instruction(
|
||||
&inner_instruction.data,
|
||||
&inner_instruction_accounts,
|
||||
Some(&caller_write_privileges),
|
||||
&program_indices,
|
||||
&mut compute_units_consumed,
|
||||
);
|
||||
|
@ -1473,7 +1463,7 @@ mod tests {
|
|||
assert_eq!(compute_units_consumed, compute_units_to_consume);
|
||||
assert_eq!(result, expected_result);
|
||||
|
||||
invoke_context.pop();
|
||||
invoke_context.pop().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1487,18 +1477,18 @@ mod tests {
|
|||
let mut feature_set = FeatureSet::all_enabled();
|
||||
feature_set.deactivate(&tx_wide_compute_cap::id());
|
||||
feature_set.deactivate(&requestable_heap_size::id());
|
||||
let transaction_context = TransactionContext::new(accounts, 1);
|
||||
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
|
||||
let mut transaction_context = TransactionContext::new(accounts, 1);
|
||||
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
invoke_context.feature_set = Arc::new(feature_set);
|
||||
|
||||
invoke_context.push(&[], &[0]).unwrap();
|
||||
invoke_context.push(&[], &[0], &[]).unwrap();
|
||||
assert_eq!(
|
||||
*invoke_context.get_compute_budget(),
|
||||
ComputeBudget::default()
|
||||
);
|
||||
invoke_context.pop();
|
||||
invoke_context.pop().unwrap();
|
||||
|
||||
invoke_context.push(&[], &[1]).unwrap();
|
||||
invoke_context.push(&[], &[1], &[]).unwrap();
|
||||
let expected_compute_budget = ComputeBudget {
|
||||
max_units: 500_000,
|
||||
heap_size: Some(256_usize.saturating_mul(1024)),
|
||||
|
@ -1508,13 +1498,13 @@ mod tests {
|
|||
*invoke_context.get_compute_budget(),
|
||||
expected_compute_budget
|
||||
);
|
||||
invoke_context.pop();
|
||||
invoke_context.pop().unwrap();
|
||||
|
||||
invoke_context.push(&[], &[0]).unwrap();
|
||||
invoke_context.push(&[], &[0], &[]).unwrap();
|
||||
assert_eq!(
|
||||
*invoke_context.get_compute_budget(),
|
||||
ComputeBudget::default()
|
||||
);
|
||||
invoke_context.pop();
|
||||
invoke_context.pop().unwrap();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -170,7 +170,7 @@ impl NativeLoader {
|
|||
invoke_context: &mut InvokeContext,
|
||||
) -> Result<(), InstructionError> {
|
||||
let (program_id, name_vec) = {
|
||||
let program_id = invoke_context.get_caller()?;
|
||||
let program_id = invoke_context.transaction_context.get_program_key()?;
|
||||
let keyed_accounts = invoke_context.get_keyed_accounts()?;
|
||||
let program = keyed_account_at_index(keyed_accounts, first_instruction_account)?;
|
||||
if native_loader::id() != *program_id {
|
||||
|
|
|
@ -45,7 +45,7 @@ use {
|
|||
solana_vote_program::vote_state::{VoteState, VoteStateVersions},
|
||||
std::{
|
||||
cell::RefCell,
|
||||
collections::HashMap,
|
||||
collections::HashSet,
|
||||
convert::TryFrom,
|
||||
fs::File,
|
||||
io::{self, Read},
|
||||
|
@ -100,57 +100,68 @@ pub fn builtin_process_instruction(
|
|||
) -> Result<(), InstructionError> {
|
||||
set_invoke_context(invoke_context);
|
||||
|
||||
let transaction_context = &invoke_context.transaction_context;
|
||||
let instruction_context = transaction_context.get_current_instruction_context()?;
|
||||
let indices_in_instruction = instruction_context.get_number_of_program_accounts()
|
||||
..instruction_context.get_number_of_accounts();
|
||||
|
||||
let log_collector = invoke_context.get_log_collector();
|
||||
let program_id = invoke_context.get_caller()?;
|
||||
let program_id = transaction_context.get_program_key()?;
|
||||
stable_log::program_invoke(&log_collector, program_id, invoke_context.invoke_depth());
|
||||
|
||||
// Skip the processor account
|
||||
let keyed_accounts = &invoke_context.get_keyed_accounts()?[1..];
|
||||
// Copy indices_in_instruction into a HashSet to ensure there are no duplicates
|
||||
let deduplicated_indices: HashSet<usize> = indices_in_instruction.clone().collect();
|
||||
|
||||
// Copy all the accounts into a HashMap to ensure there are no duplicates
|
||||
let mut accounts: HashMap<Pubkey, Account> = keyed_accounts
|
||||
// Create copies of the accounts
|
||||
let mut account_copies = deduplicated_indices
|
||||
.iter()
|
||||
.map(|ka| {
|
||||
(
|
||||
*ka.unsigned_key(),
|
||||
Account::from(ka.account.borrow().clone()),
|
||||
)
|
||||
.map(|index_in_instruction| {
|
||||
let borrowed_account = instruction_context
|
||||
.try_borrow_account(transaction_context, *index_in_instruction)?;
|
||||
Ok((
|
||||
*borrowed_account.get_key(),
|
||||
*borrowed_account.get_owner(),
|
||||
borrowed_account.get_lamports(),
|
||||
borrowed_account.get_data().to_vec(),
|
||||
))
|
||||
})
|
||||
.collect();
|
||||
.collect::<Result<Vec<_>, InstructionError>>()?;
|
||||
|
||||
// Create shared references to each account's lamports/data/owner
|
||||
let account_refs: HashMap<_, _> = accounts
|
||||
// Create shared references to account_copies
|
||||
let account_refs: Vec<_> = account_copies
|
||||
.iter_mut()
|
||||
.map(|(key, account)| {
|
||||
.map(|(key, owner, lamports, data)| {
|
||||
(
|
||||
*key,
|
||||
(
|
||||
Rc::new(RefCell::new(&mut account.lamports)),
|
||||
Rc::new(RefCell::new(&mut account.data[..])),
|
||||
&account.owner,
|
||||
),
|
||||
key,
|
||||
owner,
|
||||
Rc::new(RefCell::new(lamports)),
|
||||
Rc::new(RefCell::new(data.as_mut())),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Create AccountInfos
|
||||
let account_infos: Vec<AccountInfo> = keyed_accounts
|
||||
.iter()
|
||||
.map(|keyed_account| {
|
||||
let key = keyed_account.unsigned_key();
|
||||
let (lamports, data, owner) = &account_refs[key];
|
||||
AccountInfo {
|
||||
let account_infos = indices_in_instruction
|
||||
.map(|index_in_instruction| {
|
||||
let account_copy_index = deduplicated_indices
|
||||
.iter()
|
||||
.position(|index| *index == index_in_instruction)
|
||||
.unwrap();
|
||||
let (key, owner, lamports, data) = &account_refs[account_copy_index];
|
||||
let borrowed_account = instruction_context
|
||||
.try_borrow_account(transaction_context, index_in_instruction)?;
|
||||
Ok(AccountInfo {
|
||||
key,
|
||||
is_signer: keyed_account.signer_key().is_some(),
|
||||
is_writable: keyed_account.is_writable(),
|
||||
is_signer: borrowed_account.is_signer(),
|
||||
is_writable: borrowed_account.is_writable(),
|
||||
lamports: lamports.clone(),
|
||||
data: data.clone(),
|
||||
owner,
|
||||
executable: keyed_account.executable().unwrap(),
|
||||
rent_epoch: keyed_account.rent_epoch().unwrap(),
|
||||
}
|
||||
executable: borrowed_account.is_executable(),
|
||||
rent_epoch: borrowed_account.get_rent_epoch(),
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
.collect::<Result<Vec<AccountInfo>, InstructionError>>()?;
|
||||
|
||||
// Execute the program
|
||||
process_instruction(program_id, &account_infos, input).map_err(|err| {
|
||||
|
@ -161,12 +172,16 @@ pub fn builtin_process_instruction(
|
|||
stable_log::program_success(&log_collector, program_id);
|
||||
|
||||
// Commit AccountInfo changes back into KeyedAccounts
|
||||
for keyed_account in keyed_accounts {
|
||||
let mut account = keyed_account.account.borrow_mut();
|
||||
let key = keyed_account.unsigned_key();
|
||||
let (lamports, data, _owner) = &account_refs[key];
|
||||
account.set_lamports(**lamports.borrow());
|
||||
account.set_data(data.borrow().to_vec());
|
||||
for (index_in_instruction, (_key, _owner, lamports, data)) in deduplicated_indices
|
||||
.into_iter()
|
||||
.zip(account_copies.into_iter())
|
||||
{
|
||||
let mut borrowed_account =
|
||||
instruction_context.try_borrow_account(transaction_context, index_in_instruction)?;
|
||||
if borrowed_account.is_writable() {
|
||||
borrowed_account.set_lamports(lamports)?;
|
||||
borrowed_account.set_data(&data)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -233,7 +248,10 @@ impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs {
|
|||
let invoke_context = get_invoke_context();
|
||||
let log_collector = invoke_context.get_log_collector();
|
||||
|
||||
let caller = *invoke_context.get_caller().expect("get_caller");
|
||||
let caller = *invoke_context
|
||||
.transaction_context
|
||||
.get_program_key()
|
||||
.unwrap();
|
||||
|
||||
stable_log::program_invoke(
|
||||
&log_collector,
|
||||
|
@ -245,7 +263,7 @@ impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs {
|
|||
.iter()
|
||||
.map(|seeds| Pubkey::create_program_address(seeds, &caller).unwrap())
|
||||
.collect::<Vec<_>>();
|
||||
let (instruction_accounts, caller_write_privileges, program_indices) = invoke_context
|
||||
let (instruction_accounts, program_indices) = invoke_context
|
||||
.prepare_instruction(instruction, &signers)
|
||||
.unwrap();
|
||||
|
||||
|
@ -281,7 +299,6 @@ impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs {
|
|||
.process_instruction(
|
||||
&instruction.data,
|
||||
&instruction_accounts,
|
||||
Some(&caller_write_privileges),
|
||||
&program_indices,
|
||||
&mut compute_units_consumed,
|
||||
)
|
||||
|
|
|
@ -209,12 +209,9 @@ fn bench_create_vm(bencher: &mut Bencher) {
|
|||
.mock_set_remaining(BUDGET);
|
||||
|
||||
// Serialize account data
|
||||
let keyed_accounts = invoke_context.get_keyed_accounts().unwrap();
|
||||
let (mut serialized, account_lengths) = serialize_parameters(
|
||||
&keyed_accounts[0].unsigned_key(),
|
||||
&keyed_accounts[1].unsigned_key(),
|
||||
&keyed_accounts[2..],
|
||||
&[],
|
||||
invoke_context.transaction_context,
|
||||
invoke_context.transaction_context.get_current_instruction_context().unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
@ -250,12 +247,9 @@ fn bench_instruction_count_tuner(_bencher: &mut Bencher) {
|
|||
.mock_set_remaining(BUDGET);
|
||||
|
||||
// Serialize account data
|
||||
let keyed_accounts = invoke_context.get_keyed_accounts().unwrap();
|
||||
let (mut serialized, account_lengths) = serialize_parameters(
|
||||
&keyed_accounts[0].unsigned_key(),
|
||||
&keyed_accounts[1].unsigned_key(),
|
||||
&keyed_accounts[2..],
|
||||
&[],
|
||||
invoke_context.transaction_context,
|
||||
invoke_context.transaction_context.get_current_instruction_context().unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
|
|
@ -196,12 +196,9 @@ fn run_program(name: &str) -> u64 {
|
|||
file.read_to_end(&mut data).unwrap();
|
||||
let loader_id = bpf_loader::id();
|
||||
with_mock_invoke_context(loader_id, 0, |invoke_context| {
|
||||
let keyed_accounts = invoke_context.get_keyed_accounts().unwrap();
|
||||
let (parameter_bytes, account_lengths) = serialize_parameters(
|
||||
&keyed_accounts[0].unsigned_key(),
|
||||
&keyed_accounts[1].unsigned_key(),
|
||||
&keyed_accounts[2..],
|
||||
&[],
|
||||
invoke_context.transaction_context,
|
||||
invoke_context.transaction_context.get_current_instruction_context().unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
@ -227,7 +224,7 @@ fn run_program(name: &str) -> u64 {
|
|||
let mut instruction_count = 0;
|
||||
let mut tracer = None;
|
||||
for i in 0..2 {
|
||||
invoke_context.return_data = (*invoke_context.get_caller().unwrap(), Vec::new());
|
||||
invoke_context.return_data = (*invoke_context.transaction_context.get_program_key().unwrap(), Vec::new());
|
||||
let mut parameter_bytes = parameter_bytes.clone();
|
||||
{
|
||||
let mut vm = create_vm(
|
||||
|
@ -278,10 +275,9 @@ fn run_program(name: &str) -> u64 {
|
|||
tracer = Some(vm.get_tracer().clone());
|
||||
}
|
||||
}
|
||||
let keyed_accounts = invoke_context.get_keyed_accounts().unwrap();
|
||||
deserialize_parameters(
|
||||
&loader_id,
|
||||
&keyed_accounts[2..],
|
||||
invoke_context.transaction_context,
|
||||
invoke_context.transaction_context.get_current_instruction_context().unwrap(),
|
||||
parameter_bytes.as_slice(),
|
||||
&account_lengths,
|
||||
true,
|
||||
|
|
|
@ -9,136 +9,123 @@ use {
|
|||
solana_sdk::{
|
||||
account::{Account, AccountSharedData},
|
||||
bpf_loader,
|
||||
keyed_account::KeyedAccount,
|
||||
pubkey::Pubkey,
|
||||
transaction_context::{InstructionAccount, TransactionContext},
|
||||
},
|
||||
std::cell::RefCell,
|
||||
test::Bencher,
|
||||
};
|
||||
|
||||
fn create_inputs() -> (
|
||||
Pubkey,
|
||||
Vec<Pubkey>,
|
||||
Vec<RefCell<AccountSharedData>>,
|
||||
Vec<u8>,
|
||||
) {
|
||||
fn create_inputs() -> TransactionContext {
|
||||
let program_id = solana_sdk::pubkey::new_rand();
|
||||
let dup_key = solana_sdk::pubkey::new_rand();
|
||||
let dup_key2 = solana_sdk::pubkey::new_rand();
|
||||
let keys = vec![
|
||||
dup_key,
|
||||
dup_key,
|
||||
solana_sdk::pubkey::new_rand(),
|
||||
solana_sdk::pubkey::new_rand(),
|
||||
dup_key2,
|
||||
dup_key2,
|
||||
solana_sdk::pubkey::new_rand(),
|
||||
solana_sdk::pubkey::new_rand(),
|
||||
let transaction_accounts = vec![
|
||||
(
|
||||
program_id,
|
||||
AccountSharedData::from(Account {
|
||||
lamports: 0,
|
||||
data: vec![],
|
||||
owner: bpf_loader::id(),
|
||||
executable: true,
|
||||
rent_epoch: 0,
|
||||
}),
|
||||
),
|
||||
(
|
||||
solana_sdk::pubkey::new_rand(),
|
||||
AccountSharedData::from(Account {
|
||||
lamports: 1,
|
||||
data: vec![1u8; 100000],
|
||||
owner: bpf_loader::id(),
|
||||
executable: false,
|
||||
rent_epoch: 100,
|
||||
}),
|
||||
),
|
||||
(
|
||||
solana_sdk::pubkey::new_rand(),
|
||||
AccountSharedData::from(Account {
|
||||
lamports: 2,
|
||||
data: vec![11u8; 100000],
|
||||
owner: bpf_loader::id(),
|
||||
executable: true,
|
||||
rent_epoch: 200,
|
||||
}),
|
||||
),
|
||||
(
|
||||
solana_sdk::pubkey::new_rand(),
|
||||
AccountSharedData::from(Account {
|
||||
lamports: 3,
|
||||
data: vec![],
|
||||
owner: bpf_loader::id(),
|
||||
executable: false,
|
||||
rent_epoch: 3100,
|
||||
}),
|
||||
),
|
||||
(
|
||||
solana_sdk::pubkey::new_rand(),
|
||||
AccountSharedData::from(Account {
|
||||
lamports: 4,
|
||||
data: vec![1u8; 100000],
|
||||
owner: bpf_loader::id(),
|
||||
executable: false,
|
||||
rent_epoch: 100,
|
||||
}),
|
||||
),
|
||||
(
|
||||
solana_sdk::pubkey::new_rand(),
|
||||
AccountSharedData::from(Account {
|
||||
lamports: 5,
|
||||
data: vec![11u8; 10000],
|
||||
owner: bpf_loader::id(),
|
||||
executable: true,
|
||||
rent_epoch: 200,
|
||||
}),
|
||||
),
|
||||
(
|
||||
solana_sdk::pubkey::new_rand(),
|
||||
AccountSharedData::from(Account {
|
||||
lamports: 6,
|
||||
data: vec![],
|
||||
owner: bpf_loader::id(),
|
||||
executable: false,
|
||||
rent_epoch: 3100,
|
||||
}),
|
||||
),
|
||||
];
|
||||
let accounts = vec![
|
||||
RefCell::new(AccountSharedData::from(Account {
|
||||
lamports: 1,
|
||||
data: vec![1u8, 2, 3, 4, 5],
|
||||
owner: bpf_loader::id(),
|
||||
executable: false,
|
||||
rent_epoch: 100,
|
||||
})),
|
||||
// dup
|
||||
RefCell::new(AccountSharedData::from(Account {
|
||||
lamports: 1,
|
||||
data: vec![1u8; 100000],
|
||||
owner: bpf_loader::id(),
|
||||
executable: false,
|
||||
rent_epoch: 100,
|
||||
})),
|
||||
RefCell::new(AccountSharedData::from(Account {
|
||||
lamports: 2,
|
||||
data: vec![11u8; 100000],
|
||||
owner: bpf_loader::id(),
|
||||
executable: true,
|
||||
rent_epoch: 200,
|
||||
})),
|
||||
RefCell::new(AccountSharedData::from(Account {
|
||||
lamports: 3,
|
||||
data: vec![],
|
||||
owner: bpf_loader::id(),
|
||||
executable: false,
|
||||
rent_epoch: 3100,
|
||||
})),
|
||||
RefCell::new(AccountSharedData::from(Account {
|
||||
lamports: 4,
|
||||
data: vec![1u8; 100000],
|
||||
owner: bpf_loader::id(),
|
||||
executable: false,
|
||||
rent_epoch: 100,
|
||||
})),
|
||||
// dup
|
||||
RefCell::new(AccountSharedData::from(Account {
|
||||
lamports: 4,
|
||||
data: vec![1u8; 1000000],
|
||||
owner: bpf_loader::id(),
|
||||
executable: false,
|
||||
rent_epoch: 100,
|
||||
})),
|
||||
RefCell::new(AccountSharedData::from(Account {
|
||||
lamports: 5,
|
||||
data: vec![11u8; 10000],
|
||||
owner: bpf_loader::id(),
|
||||
executable: true,
|
||||
rent_epoch: 200,
|
||||
})),
|
||||
RefCell::new(AccountSharedData::from(Account {
|
||||
lamports: 6,
|
||||
data: vec![],
|
||||
owner: bpf_loader::id(),
|
||||
executable: false,
|
||||
rent_epoch: 3100,
|
||||
})),
|
||||
];
|
||||
|
||||
let instruction_accounts = [1, 1, 2, 3, 4, 4, 5, 6]
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(
|
||||
|(index_in_instruction, index_in_transaction)| InstructionAccount {
|
||||
index_in_caller: 1usize.saturating_add(index_in_instruction),
|
||||
index_in_transaction,
|
||||
is_signer: false,
|
||||
is_writable: index_in_instruction >= 4,
|
||||
},
|
||||
)
|
||||
.collect::<Vec<_>>();
|
||||
let mut transaction_context = TransactionContext::new(transaction_accounts, 1);
|
||||
let instruction_data = vec![1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
|
||||
|
||||
(program_id, keys, accounts, instruction_data)
|
||||
transaction_context
|
||||
.push(&[0], &instruction_accounts, &instruction_data)
|
||||
.unwrap();
|
||||
transaction_context
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_serialize_unaligned(bencher: &mut Bencher) {
|
||||
let (program_id, keys, accounts, instruction_data) = create_inputs();
|
||||
let keyed_accounts: Vec<_> = keys
|
||||
.iter()
|
||||
.zip(&accounts)
|
||||
.enumerate()
|
||||
.map(|(i, (key, account))| {
|
||||
if i <= accounts.len() / 2 {
|
||||
KeyedAccount::new_readonly(key, false, account)
|
||||
} else {
|
||||
KeyedAccount::new(key, false, account)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let transaction_context = create_inputs();
|
||||
let instruction_context = transaction_context
|
||||
.get_current_instruction_context()
|
||||
.unwrap();
|
||||
bencher.iter(|| {
|
||||
let _ = serialize_parameters_unaligned(&program_id, &keyed_accounts, &instruction_data)
|
||||
.unwrap();
|
||||
let _ = serialize_parameters_unaligned(&transaction_context, instruction_context).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_serialize_aligned(bencher: &mut Bencher) {
|
||||
let (program_id, keys, accounts, instruction_data) = create_inputs();
|
||||
let keyed_accounts: Vec<_> = keys
|
||||
.iter()
|
||||
.zip(&accounts)
|
||||
.enumerate()
|
||||
.map(|(i, (key, account))| {
|
||||
if i <= accounts.len() / 2 {
|
||||
KeyedAccount::new_readonly(key, false, account)
|
||||
} else {
|
||||
KeyedAccount::new(key, false, account)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let transaction_context = create_inputs();
|
||||
let instruction_context = transaction_context
|
||||
.get_current_instruction_context()
|
||||
.unwrap();
|
||||
bencher.iter(|| {
|
||||
let _ =
|
||||
serialize_parameters_aligned(&program_id, &keyed_accounts, &instruction_data).unwrap();
|
||||
let _ = serialize_parameters_aligned(&transaction_context, instruction_context).unwrap();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -225,7 +225,7 @@ fn process_instruction_common(
|
|||
use_jit: bool,
|
||||
) -> Result<(), InstructionError> {
|
||||
let log_collector = invoke_context.get_log_collector();
|
||||
let program_id = invoke_context.get_caller()?;
|
||||
let program_id = invoke_context.transaction_context.get_program_key()?;
|
||||
|
||||
let keyed_accounts = invoke_context.get_keyed_accounts()?;
|
||||
let first_account = keyed_account_at_index(keyed_accounts, first_instruction_account)?;
|
||||
|
@ -301,7 +301,7 @@ fn process_instruction_common(
|
|||
use_jit,
|
||||
false,
|
||||
)?;
|
||||
let program_id = invoke_context.get_caller()?;
|
||||
let program_id = invoke_context.transaction_context.get_program_key()?;
|
||||
invoke_context.add_executor(program_id, executor.clone());
|
||||
executor
|
||||
}
|
||||
|
@ -342,7 +342,7 @@ fn process_loader_upgradeable_instruction(
|
|||
use_jit: bool,
|
||||
) -> Result<(), InstructionError> {
|
||||
let log_collector = invoke_context.get_log_collector();
|
||||
let program_id = invoke_context.get_caller()?;
|
||||
let program_id = invoke_context.transaction_context.get_program_key()?;
|
||||
let keyed_accounts = invoke_context.get_keyed_accounts()?;
|
||||
|
||||
match limited_deserialize(instruction_data)? {
|
||||
|
@ -495,7 +495,7 @@ fn process_loader_upgradeable_instruction(
|
|||
.accounts
|
||||
.push(AccountMeta::new(*buffer.unsigned_key(), false));
|
||||
|
||||
let caller_program_id = invoke_context.get_caller()?;
|
||||
let caller_program_id = invoke_context.transaction_context.get_program_key()?;
|
||||
let signers = [&[new_program_id.as_ref(), &[bump_seed]]]
|
||||
.iter()
|
||||
.map(|seeds| Pubkey::create_program_address(*seeds, caller_program_id))
|
||||
|
@ -892,7 +892,7 @@ fn process_loader_instruction(
|
|||
invoke_context: &mut InvokeContext,
|
||||
use_jit: bool,
|
||||
) -> Result<(), InstructionError> {
|
||||
let program_id = invoke_context.get_caller()?;
|
||||
let program_id = invoke_context.transaction_context.get_program_key()?;
|
||||
let keyed_accounts = invoke_context.get_keyed_accounts()?;
|
||||
let program = keyed_account_at_index(keyed_accounts, first_instruction_account)?;
|
||||
if program.owner()? != *program_id {
|
||||
|
@ -973,8 +973,8 @@ impl Debug for BpfExecutor {
|
|||
impl Executor for BpfExecutor {
|
||||
fn execute<'a, 'b>(
|
||||
&self,
|
||||
first_instruction_account: usize,
|
||||
instruction_data: &[u8],
|
||||
_first_instruction_account: usize,
|
||||
_instruction_data: &[u8],
|
||||
invoke_context: &'a mut InvokeContext<'b>,
|
||||
use_jit: bool,
|
||||
) -> Result<(), InstructionError> {
|
||||
|
@ -983,15 +983,12 @@ impl Executor for BpfExecutor {
|
|||
let invoke_depth = invoke_context.invoke_depth();
|
||||
|
||||
let mut serialize_time = Measure::start("serialize");
|
||||
let keyed_accounts = invoke_context.get_keyed_accounts()?;
|
||||
let program = keyed_account_at_index(keyed_accounts, first_instruction_account)?;
|
||||
let loader_id = program.owner()?;
|
||||
let program_id = *program.unsigned_key();
|
||||
let program_id = *invoke_context.transaction_context.get_program_key()?;
|
||||
let (mut parameter_bytes, account_lengths) = serialize_parameters(
|
||||
&loader_id,
|
||||
&program_id,
|
||||
&keyed_accounts[first_instruction_account + 1..],
|
||||
instruction_data,
|
||||
invoke_context.transaction_context,
|
||||
invoke_context
|
||||
.transaction_context
|
||||
.get_current_instruction_context()?,
|
||||
)?;
|
||||
serialize_time.stop();
|
||||
let mut create_vm_time = Measure::start("create_vm");
|
||||
|
@ -1076,10 +1073,11 @@ impl Executor for BpfExecutor {
|
|||
|
||||
let mut deserialize_time = Measure::start("deserialize");
|
||||
let execute_or_deserialize_result = execution_result.and_then(|_| {
|
||||
let keyed_accounts = invoke_context.get_keyed_accounts()?;
|
||||
deserialize_parameters(
|
||||
&loader_id,
|
||||
&keyed_accounts[first_instruction_account + 1..],
|
||||
invoke_context.transaction_context,
|
||||
invoke_context
|
||||
.transaction_context
|
||||
.get_current_instruction_context()?,
|
||||
parameter_bytes.as_slice(),
|
||||
&account_lengths,
|
||||
invoke_context
|
||||
|
|
|
@ -2,58 +2,79 @@ use {
|
|||
byteorder::{ByteOrder, LittleEndian, WriteBytesExt},
|
||||
solana_rbpf::{aligned_memory::AlignedMemory, ebpf::HOST_ALIGN},
|
||||
solana_sdk::{
|
||||
account::{ReadableAccount, WritableAccount},
|
||||
bpf_loader_deprecated,
|
||||
entrypoint::{BPF_ALIGN_OF_U128, MAX_PERMITTED_DATA_INCREASE},
|
||||
instruction::InstructionError,
|
||||
keyed_account::KeyedAccount,
|
||||
pubkey::Pubkey,
|
||||
system_instruction::MAX_PERMITTED_DATA_LENGTH,
|
||||
transaction_context::{InstructionContext, TransactionContext},
|
||||
},
|
||||
std::{io::prelude::*, mem::size_of},
|
||||
};
|
||||
|
||||
/// Look for a duplicate account and return its position if found
|
||||
pub fn is_dup(accounts: &[KeyedAccount], keyed_account: &KeyedAccount) -> (bool, usize) {
|
||||
for (i, account) in accounts.iter().enumerate() {
|
||||
if account == keyed_account {
|
||||
return (true, i);
|
||||
}
|
||||
}
|
||||
(false, 0)
|
||||
pub fn is_duplicate(
|
||||
instruction_context: &InstructionContext,
|
||||
index_in_instruction: usize,
|
||||
) -> Option<usize> {
|
||||
let index_in_transaction = instruction_context.get_index_in_transaction(index_in_instruction);
|
||||
(instruction_context.get_number_of_program_accounts()..index_in_instruction).position(
|
||||
|index_in_instruction| {
|
||||
instruction_context.get_index_in_transaction(index_in_instruction)
|
||||
== index_in_transaction
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn serialize_parameters(
|
||||
loader_id: &Pubkey,
|
||||
program_id: &Pubkey,
|
||||
keyed_accounts: &[KeyedAccount],
|
||||
data: &[u8],
|
||||
transaction_context: &TransactionContext,
|
||||
instruction_context: &InstructionContext,
|
||||
) -> Result<(AlignedMemory, Vec<usize>), InstructionError> {
|
||||
if *loader_id == bpf_loader_deprecated::id() {
|
||||
serialize_parameters_unaligned(program_id, keyed_accounts, data)
|
||||
let is_loader_deprecated = *instruction_context
|
||||
.try_borrow_program_account(transaction_context)?
|
||||
.get_owner()
|
||||
== bpf_loader_deprecated::id();
|
||||
if is_loader_deprecated {
|
||||
serialize_parameters_unaligned(transaction_context, instruction_context)
|
||||
} else {
|
||||
serialize_parameters_aligned(program_id, keyed_accounts, data)
|
||||
serialize_parameters_aligned(transaction_context, instruction_context)
|
||||
}
|
||||
.and_then(|buffer| {
|
||||
let account_lengths = keyed_accounts
|
||||
.iter()
|
||||
.map(|keyed_account| keyed_account.data_len())
|
||||
let account_lengths = (instruction_context.get_number_of_program_accounts()
|
||||
..instruction_context.get_number_of_accounts())
|
||||
.map(|index_in_instruction| {
|
||||
Ok(instruction_context
|
||||
.try_borrow_account(transaction_context, index_in_instruction)?
|
||||
.get_data()
|
||||
.len())
|
||||
})
|
||||
.collect::<Result<Vec<usize>, InstructionError>>()?;
|
||||
Ok((buffer, account_lengths))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn deserialize_parameters(
|
||||
loader_id: &Pubkey,
|
||||
keyed_accounts: &[KeyedAccount],
|
||||
transaction_context: &TransactionContext,
|
||||
instruction_context: &InstructionContext,
|
||||
buffer: &[u8],
|
||||
account_lengths: &[usize],
|
||||
do_support_realloc: bool,
|
||||
) -> Result<(), InstructionError> {
|
||||
if *loader_id == bpf_loader_deprecated::id() {
|
||||
deserialize_parameters_unaligned(keyed_accounts, buffer, account_lengths)
|
||||
let is_loader_deprecated = *instruction_context
|
||||
.try_borrow_program_account(transaction_context)?
|
||||
.get_owner()
|
||||
== bpf_loader_deprecated::id();
|
||||
if is_loader_deprecated {
|
||||
deserialize_parameters_unaligned(transaction_context, instruction_context, buffer)
|
||||
} else {
|
||||
deserialize_parameters_aligned(keyed_accounts, buffer, account_lengths, do_support_realloc)
|
||||
deserialize_parameters_aligned(
|
||||
transaction_context,
|
||||
instruction_context,
|
||||
buffer,
|
||||
account_lengths,
|
||||
do_support_realloc,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -75,90 +96,108 @@ pub fn get_serialized_account_size_unaligned(
|
|||
}
|
||||
|
||||
pub fn serialize_parameters_unaligned(
|
||||
program_id: &Pubkey,
|
||||
keyed_accounts: &[KeyedAccount],
|
||||
instruction_data: &[u8],
|
||||
transaction_context: &TransactionContext,
|
||||
instruction_context: &InstructionContext,
|
||||
) -> Result<AlignedMemory, InstructionError> {
|
||||
// Calculate size in order to alloc once
|
||||
let mut size = size_of::<u64>();
|
||||
for (i, keyed_account) in keyed_accounts.iter().enumerate() {
|
||||
let (is_dup, _) = is_dup(&keyed_accounts[..i], keyed_account);
|
||||
for index_in_instruction in instruction_context.get_number_of_program_accounts()
|
||||
..instruction_context.get_number_of_accounts()
|
||||
{
|
||||
let duplicate = is_duplicate(instruction_context, index_in_instruction);
|
||||
size += 1; // dup
|
||||
if !is_dup {
|
||||
size += get_serialized_account_size_unaligned(keyed_account)?;
|
||||
if duplicate.is_none() {
|
||||
let data_len = instruction_context
|
||||
.try_borrow_account(transaction_context, index_in_instruction)?
|
||||
.get_data()
|
||||
.len();
|
||||
size += size_of::<u8>() // is_signer
|
||||
+ size_of::<u8>() // is_writable
|
||||
+ size_of::<Pubkey>() // key
|
||||
+ size_of::<u64>() // lamports
|
||||
+ size_of::<u64>() // data len
|
||||
+ data_len // data
|
||||
+ size_of::<Pubkey>() // owner
|
||||
+ size_of::<u8>() // executable
|
||||
+ size_of::<u64>(); // rent_epoch
|
||||
}
|
||||
}
|
||||
size += size_of::<u64>() // instruction data len
|
||||
+ instruction_data.len() // instruction data
|
||||
+ instruction_context.get_instruction_data().len() // instruction data
|
||||
+ size_of::<Pubkey>(); // program id
|
||||
let mut v = AlignedMemory::new(size, HOST_ALIGN);
|
||||
|
||||
v.write_u64::<LittleEndian>(keyed_accounts.len() as u64)
|
||||
v.write_u64::<LittleEndian>(instruction_context.get_number_of_instruction_accounts() as u64)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
for (i, keyed_account) in keyed_accounts.iter().enumerate() {
|
||||
let (is_dup, position) = is_dup(&keyed_accounts[..i], keyed_account);
|
||||
if is_dup {
|
||||
for index_in_instruction in instruction_context.get_number_of_program_accounts()
|
||||
..instruction_context.get_number_of_accounts()
|
||||
{
|
||||
let duplicate = is_duplicate(instruction_context, index_in_instruction);
|
||||
if let Some(position) = duplicate {
|
||||
v.write_u8(position as u8)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
} else {
|
||||
let borrowed_account = instruction_context
|
||||
.try_borrow_account(transaction_context, index_in_instruction)?;
|
||||
v.write_u8(std::u8::MAX)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_u8(keyed_account.signer_key().is_some() as u8)
|
||||
v.write_u8(borrowed_account.is_signer() as u8)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_u8(keyed_account.is_writable() as u8)
|
||||
v.write_u8(borrowed_account.is_writable() as u8)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_all(keyed_account.unsigned_key().as_ref())
|
||||
v.write_all(borrowed_account.get_key().as_ref())
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_u64::<LittleEndian>(keyed_account.lamports()?)
|
||||
v.write_u64::<LittleEndian>(borrowed_account.get_lamports())
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_u64::<LittleEndian>(keyed_account.data_len()? as u64)
|
||||
v.write_u64::<LittleEndian>(borrowed_account.get_data().len() as u64)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_all(keyed_account.try_account_ref()?.data())
|
||||
v.write_all(borrowed_account.get_data())
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_all(keyed_account.owner()?.as_ref())
|
||||
v.write_all(borrowed_account.get_owner().as_ref())
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_u8(keyed_account.executable()? as u8)
|
||||
v.write_u8(borrowed_account.is_executable() as u8)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_u64::<LittleEndian>(keyed_account.rent_epoch()? as u64)
|
||||
v.write_u64::<LittleEndian>(borrowed_account.get_rent_epoch() as u64)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
}
|
||||
}
|
||||
v.write_u64::<LittleEndian>(instruction_data.len() as u64)
|
||||
v.write_u64::<LittleEndian>(instruction_context.get_instruction_data().len() as u64)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_all(instruction_data)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_all(program_id.as_ref())
|
||||
v.write_all(instruction_context.get_instruction_data())
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_all(
|
||||
instruction_context
|
||||
.try_borrow_program_account(transaction_context)?
|
||||
.get_key()
|
||||
.as_ref(),
|
||||
)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
pub fn deserialize_parameters_unaligned(
|
||||
keyed_accounts: &[KeyedAccount],
|
||||
transaction_context: &TransactionContext,
|
||||
instruction_context: &InstructionContext,
|
||||
buffer: &[u8],
|
||||
account_lengths: &[usize],
|
||||
) -> Result<(), InstructionError> {
|
||||
let mut start = size_of::<u64>(); // number of accounts
|
||||
for (i, (keyed_account, _pre_len)) in keyed_accounts
|
||||
.iter()
|
||||
.zip(account_lengths.iter())
|
||||
.enumerate()
|
||||
for index_in_instruction in instruction_context.get_number_of_program_accounts()
|
||||
..instruction_context.get_number_of_accounts()
|
||||
{
|
||||
let (is_dup, _) = is_dup(&keyed_accounts[..i], keyed_account);
|
||||
let duplicate = is_duplicate(instruction_context, index_in_instruction);
|
||||
start += 1; // is_dup
|
||||
if !is_dup {
|
||||
if duplicate.is_none() {
|
||||
let mut borrowed_account = instruction_context
|
||||
.try_borrow_account(transaction_context, index_in_instruction)?;
|
||||
start += size_of::<u8>(); // is_signer
|
||||
start += size_of::<u8>(); // is_writable
|
||||
start += size_of::<Pubkey>(); // key
|
||||
keyed_account
|
||||
.try_account_ref_mut()?
|
||||
.set_lamports(LittleEndian::read_u64(&buffer[start..]));
|
||||
let _ = borrowed_account.set_lamports(LittleEndian::read_u64(&buffer[start..]));
|
||||
start += size_of::<u64>() // lamports
|
||||
+ size_of::<u64>(); // data length
|
||||
let end = start + keyed_account.data_len()?;
|
||||
keyed_account
|
||||
.try_account_ref_mut()?
|
||||
.set_data_from_slice(&buffer[start..end]);
|
||||
start += keyed_account.data_len()? // data
|
||||
let end = start + borrowed_account.get_data().len();
|
||||
let _ = borrowed_account.set_data(&buffer[start..end]);
|
||||
start += borrowed_account.get_data().len() // data
|
||||
+ size_of::<Pubkey>() // owner
|
||||
+ size_of::<u8>() // executable
|
||||
+ size_of::<u64>(); // rent_epoch
|
||||
|
@ -167,77 +206,77 @@ pub fn deserialize_parameters_unaligned(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_serialized_account_size_aligned(
|
||||
keyed_account: &KeyedAccount,
|
||||
) -> Result<usize, InstructionError> {
|
||||
let data_len = keyed_account.data_len()?;
|
||||
Ok(
|
||||
size_of::<u8>() // is_signer
|
||||
+ size_of::<u8>() // is_writable
|
||||
+ size_of::<u8>() // executable
|
||||
+ 4 // padding to 128-bit aligned
|
||||
+ size_of::<Pubkey>() // key
|
||||
+ size_of::<Pubkey>() // owner
|
||||
+ size_of::<u64>() // lamports
|
||||
+ size_of::<u64>() // data len
|
||||
+ data_len
|
||||
+ MAX_PERMITTED_DATA_INCREASE
|
||||
+ (data_len as *const u8).align_offset(BPF_ALIGN_OF_U128)
|
||||
+ size_of::<u64>(), // rent epoch
|
||||
)
|
||||
}
|
||||
|
||||
pub fn serialize_parameters_aligned(
|
||||
program_id: &Pubkey,
|
||||
keyed_accounts: &[KeyedAccount],
|
||||
instruction_data: &[u8],
|
||||
transaction_context: &TransactionContext,
|
||||
instruction_context: &InstructionContext,
|
||||
) -> Result<AlignedMemory, InstructionError> {
|
||||
// Calculate size in order to alloc once
|
||||
let mut size = size_of::<u64>();
|
||||
for (i, keyed_account) in keyed_accounts.iter().enumerate() {
|
||||
let (is_dup, _) = is_dup(&keyed_accounts[..i], keyed_account);
|
||||
for index_in_instruction in instruction_context.get_number_of_program_accounts()
|
||||
..instruction_context.get_number_of_accounts()
|
||||
{
|
||||
let duplicate = is_duplicate(instruction_context, index_in_instruction);
|
||||
size += 1; // dup
|
||||
if is_dup {
|
||||
if duplicate.is_some() {
|
||||
size += 7; // padding to 64-bit aligned
|
||||
} else {
|
||||
size += get_serialized_account_size_aligned(keyed_account)?;
|
||||
let data_len = instruction_context
|
||||
.try_borrow_account(transaction_context, index_in_instruction)?
|
||||
.get_data()
|
||||
.len();
|
||||
size += size_of::<u8>() // is_signer
|
||||
+ size_of::<u8>() // is_writable
|
||||
+ size_of::<u8>() // executable
|
||||
+ 4 // padding to 128-bit aligned
|
||||
+ size_of::<Pubkey>() // key
|
||||
+ size_of::<Pubkey>() // owner
|
||||
+ size_of::<u64>() // lamports
|
||||
+ size_of::<u64>() // data len
|
||||
+ data_len
|
||||
+ MAX_PERMITTED_DATA_INCREASE
|
||||
+ (data_len as *const u8).align_offset(BPF_ALIGN_OF_U128)
|
||||
+ size_of::<u64>(); // rent epoch
|
||||
}
|
||||
}
|
||||
size += size_of::<u64>() // data len
|
||||
+ instruction_data.len()
|
||||
+ instruction_context.get_instruction_data().len()
|
||||
+ size_of::<Pubkey>(); // program id;
|
||||
let mut v = AlignedMemory::new(size, HOST_ALIGN);
|
||||
|
||||
// Serialize into the buffer
|
||||
v.write_u64::<LittleEndian>(keyed_accounts.len() as u64)
|
||||
v.write_u64::<LittleEndian>(instruction_context.get_number_of_instruction_accounts() as u64)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
for (i, keyed_account) in keyed_accounts.iter().enumerate() {
|
||||
let (is_dup, position) = is_dup(&keyed_accounts[..i], keyed_account);
|
||||
if is_dup {
|
||||
for index_in_instruction in instruction_context.get_number_of_program_accounts()
|
||||
..instruction_context.get_number_of_accounts()
|
||||
{
|
||||
let duplicate = is_duplicate(instruction_context, index_in_instruction);
|
||||
if let Some(position) = duplicate {
|
||||
v.write_u8(position as u8)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_all(&[0u8, 0, 0, 0, 0, 0, 0])
|
||||
.map_err(|_| InstructionError::InvalidArgument)?; // 7 bytes of padding to make 64-bit aligned
|
||||
} else {
|
||||
let borrowed_account = instruction_context
|
||||
.try_borrow_account(transaction_context, index_in_instruction)?;
|
||||
v.write_u8(std::u8::MAX)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_u8(keyed_account.signer_key().is_some() as u8)
|
||||
v.write_u8(borrowed_account.is_signer() as u8)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_u8(keyed_account.is_writable() as u8)
|
||||
v.write_u8(borrowed_account.is_writable() as u8)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_u8(keyed_account.executable()? as u8)
|
||||
v.write_u8(borrowed_account.is_executable() as u8)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_all(&[0u8, 0, 0, 0])
|
||||
.map_err(|_| InstructionError::InvalidArgument)?; // 4 bytes of padding to make 128-bit aligned
|
||||
v.write_all(keyed_account.unsigned_key().as_ref())
|
||||
v.write_all(borrowed_account.get_key().as_ref())
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_all(keyed_account.owner()?.as_ref())
|
||||
v.write_all(borrowed_account.get_owner().as_ref())
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_u64::<LittleEndian>(keyed_account.lamports()?)
|
||||
v.write_u64::<LittleEndian>(borrowed_account.get_lamports())
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_u64::<LittleEndian>(keyed_account.data_len()? as u64)
|
||||
v.write_u64::<LittleEndian>(borrowed_account.get_data().len() as u64)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_all(keyed_account.try_account_ref()?.data())
|
||||
v.write_all(borrowed_account.get_data())
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.resize(
|
||||
MAX_PERMITTED_DATA_INCREASE
|
||||
|
@ -245,45 +284,51 @@ pub fn serialize_parameters_aligned(
|
|||
0,
|
||||
)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_u64::<LittleEndian>(keyed_account.rent_epoch()? as u64)
|
||||
v.write_u64::<LittleEndian>(borrowed_account.get_rent_epoch() as u64)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
}
|
||||
}
|
||||
v.write_u64::<LittleEndian>(instruction_data.len() as u64)
|
||||
v.write_u64::<LittleEndian>(instruction_context.get_instruction_data().len() as u64)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_all(instruction_data)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_all(program_id.as_ref())
|
||||
v.write_all(instruction_context.get_instruction_data())
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_all(
|
||||
instruction_context
|
||||
.try_borrow_program_account(transaction_context)?
|
||||
.get_key()
|
||||
.as_ref(),
|
||||
)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
pub fn deserialize_parameters_aligned(
|
||||
keyed_accounts: &[KeyedAccount],
|
||||
transaction_context: &TransactionContext,
|
||||
instruction_context: &InstructionContext,
|
||||
buffer: &[u8],
|
||||
account_lengths: &[usize],
|
||||
do_support_realloc: bool,
|
||||
) -> Result<(), InstructionError> {
|
||||
let mut start = size_of::<u64>(); // number of accounts
|
||||
for (i, (keyed_account, pre_len)) in keyed_accounts
|
||||
.iter()
|
||||
for (index_in_instruction, pre_len) in (instruction_context.get_number_of_program_accounts()
|
||||
..instruction_context.get_number_of_accounts())
|
||||
.zip(account_lengths.iter())
|
||||
.enumerate()
|
||||
{
|
||||
let (is_dup, _) = is_dup(&keyed_accounts[..i], keyed_account);
|
||||
let duplicate = is_duplicate(instruction_context, index_in_instruction);
|
||||
start += size_of::<u8>(); // position
|
||||
if is_dup {
|
||||
if duplicate.is_some() {
|
||||
start += 7; // padding to 64-bit aligned
|
||||
} else {
|
||||
let mut account = keyed_account.try_account_ref_mut()?;
|
||||
let mut borrowed_account = instruction_context
|
||||
.try_borrow_account(transaction_context, index_in_instruction)?;
|
||||
start += size_of::<u8>() // is_signer
|
||||
+ size_of::<u8>() // is_writable
|
||||
+ size_of::<u8>() // executable
|
||||
+ 4 // padding to 128-bit aligned
|
||||
+ size_of::<Pubkey>(); // key
|
||||
account.copy_into_owner_from_slice(&buffer[start..start + size_of::<Pubkey>()]);
|
||||
let _ = borrowed_account.set_owner(&buffer[start..start + size_of::<Pubkey>()]);
|
||||
start += size_of::<Pubkey>(); // owner
|
||||
account.set_lamports(LittleEndian::read_u64(&buffer[start..]));
|
||||
let _ = borrowed_account.set_lamports(LittleEndian::read_u64(&buffer[start..]));
|
||||
start += size_of::<u64>(); // lamports
|
||||
let post_len = LittleEndian::read_u64(&buffer[start..]) as usize;
|
||||
start += size_of::<u64>(); // data length
|
||||
|
@ -303,7 +348,7 @@ pub fn deserialize_parameters_aligned(
|
|||
}
|
||||
data_end
|
||||
};
|
||||
account.set_data_from_slice(&buffer[start..data_end]);
|
||||
let _ = borrowed_account.set_data(&buffer[start..data_end]);
|
||||
start += *pre_len + MAX_PERMITTED_DATA_INCREASE; // data
|
||||
start += (start as *const u8).align_offset(BPF_ALIGN_OF_U128);
|
||||
start += size_of::<u64>(); // rent_epoch
|
||||
|
@ -318,12 +363,11 @@ mod tests {
|
|||
super::*,
|
||||
solana_program_runtime::invoke_context::{prepare_mock_invoke_context, InvokeContext},
|
||||
solana_sdk::{
|
||||
account::{Account, AccountSharedData},
|
||||
account::{Account, AccountSharedData, ReadableAccount},
|
||||
account_info::AccountInfo,
|
||||
bpf_loader,
|
||||
entrypoint::deserialize,
|
||||
instruction::AccountMeta,
|
||||
transaction_context::TransactionContext,
|
||||
},
|
||||
std::{
|
||||
cell::RefCell,
|
||||
|
@ -407,71 +451,40 @@ mod tests {
|
|||
}),
|
||||
),
|
||||
];
|
||||
let instruction_accounts = vec![
|
||||
AccountMeta {
|
||||
pubkey: transaction_accounts[1].0,
|
||||
let instruction_accounts = [1, 1, 2, 3, 4, 4, 5, 6]
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(index_in_instruction, index_in_transaction)| AccountMeta {
|
||||
pubkey: transaction_accounts[index_in_transaction].0,
|
||||
is_signer: false,
|
||||
is_writable: false,
|
||||
},
|
||||
AccountMeta {
|
||||
pubkey: transaction_accounts[1].0,
|
||||
is_signer: false,
|
||||
is_writable: false,
|
||||
},
|
||||
AccountMeta {
|
||||
pubkey: transaction_accounts[2].0,
|
||||
is_signer: false,
|
||||
is_writable: false,
|
||||
},
|
||||
AccountMeta {
|
||||
pubkey: transaction_accounts[3].0,
|
||||
is_signer: false,
|
||||
is_writable: false,
|
||||
},
|
||||
AccountMeta {
|
||||
pubkey: transaction_accounts[4].0,
|
||||
is_signer: false,
|
||||
is_writable: true,
|
||||
},
|
||||
AccountMeta {
|
||||
pubkey: transaction_accounts[4].0,
|
||||
is_signer: false,
|
||||
is_writable: true,
|
||||
},
|
||||
AccountMeta {
|
||||
pubkey: transaction_accounts[5].0,
|
||||
is_signer: false,
|
||||
is_writable: true,
|
||||
},
|
||||
AccountMeta {
|
||||
pubkey: transaction_accounts[6].0,
|
||||
is_signer: false,
|
||||
is_writable: true,
|
||||
},
|
||||
];
|
||||
is_writable: index_in_instruction >= 4,
|
||||
})
|
||||
.collect();
|
||||
let instruction_data = vec![1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
|
||||
let program_indices = [0];
|
||||
let preparation = prepare_mock_invoke_context(
|
||||
transaction_accounts.clone(),
|
||||
transaction_accounts,
|
||||
instruction_accounts,
|
||||
&program_indices,
|
||||
);
|
||||
let transaction_context = TransactionContext::new(preparation.transaction_accounts, 1);
|
||||
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
|
||||
let mut transaction_context = TransactionContext::new(preparation.transaction_accounts, 1);
|
||||
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
invoke_context
|
||||
.push(&preparation.instruction_accounts, &program_indices)
|
||||
.push(
|
||||
&preparation.instruction_accounts,
|
||||
&program_indices,
|
||||
&instruction_data,
|
||||
)
|
||||
.unwrap();
|
||||
let instruction_context = invoke_context
|
||||
.transaction_context
|
||||
.get_current_instruction_context()
|
||||
.unwrap();
|
||||
|
||||
// check serialize_parameters_aligned
|
||||
|
||||
let ser_keyed_accounts = invoke_context.get_keyed_accounts().unwrap();
|
||||
let (mut serialized, account_lengths) = serialize_parameters(
|
||||
&bpf_loader::id(),
|
||||
&program_id,
|
||||
&ser_keyed_accounts[1..],
|
||||
&instruction_data,
|
||||
)
|
||||
.unwrap();
|
||||
let (mut serialized, account_lengths) =
|
||||
serialize_parameters(invoke_context.transaction_context, instruction_context).unwrap();
|
||||
|
||||
let (de_program_id, de_accounts, de_instruction_data) =
|
||||
unsafe { deserialize(&mut serialized.as_slice_mut()[0] as *mut u8) };
|
||||
|
@ -483,11 +496,14 @@ mod tests {
|
|||
0
|
||||
);
|
||||
for account_info in de_accounts {
|
||||
let account = &transaction_accounts
|
||||
.iter()
|
||||
.find(|(key, _account)| key == account_info.key)
|
||||
.unwrap()
|
||||
.1;
|
||||
let index_in_transaction = invoke_context
|
||||
.transaction_context
|
||||
.find_index_of_account(account_info.key)
|
||||
.unwrap();
|
||||
let account = invoke_context
|
||||
.transaction_context
|
||||
.get_account_at_index(index_in_transaction)
|
||||
.borrow();
|
||||
assert_eq!(account.lamports(), account_info.lamports());
|
||||
assert_eq!(account.data(), &account_info.data.borrow()[..]);
|
||||
assert_eq!(account.owner(), account_info.owner);
|
||||
|
@ -510,44 +526,51 @@ mod tests {
|
|||
|
||||
let de_keyed_accounts = invoke_context.get_keyed_accounts().unwrap();
|
||||
deserialize_parameters(
|
||||
&bpf_loader::id(),
|
||||
&de_keyed_accounts[1..],
|
||||
invoke_context.transaction_context,
|
||||
instruction_context,
|
||||
serialized.as_slice(),
|
||||
&account_lengths,
|
||||
true,
|
||||
)
|
||||
.unwrap();
|
||||
for keyed_account in de_keyed_accounts {
|
||||
let account = &transaction_accounts
|
||||
.iter()
|
||||
.find(|(key, _account)| key == keyed_account.unsigned_key())
|
||||
.unwrap()
|
||||
.1;
|
||||
let index_in_transaction = invoke_context
|
||||
.transaction_context
|
||||
.find_index_of_account(keyed_account.unsigned_key())
|
||||
.unwrap();
|
||||
let account = invoke_context
|
||||
.transaction_context
|
||||
.get_account_at_index(index_in_transaction)
|
||||
.borrow();
|
||||
assert_eq!(account.executable(), keyed_account.executable().unwrap());
|
||||
assert_eq!(account.rent_epoch(), keyed_account.rent_epoch().unwrap());
|
||||
}
|
||||
|
||||
// check serialize_parameters_unaligned
|
||||
let _ = invoke_context
|
||||
.transaction_context
|
||||
.get_current_instruction_context()
|
||||
.unwrap()
|
||||
.try_borrow_account(invoke_context.transaction_context, 0)
|
||||
.unwrap()
|
||||
.set_owner(bpf_loader_deprecated::id().as_ref());
|
||||
|
||||
let ser_keyed_accounts = invoke_context.get_keyed_accounts().unwrap();
|
||||
let (mut serialized, account_lengths) = serialize_parameters(
|
||||
&bpf_loader_deprecated::id(),
|
||||
&program_id,
|
||||
&ser_keyed_accounts[1..],
|
||||
&instruction_data,
|
||||
)
|
||||
.unwrap();
|
||||
let (mut serialized, account_lengths) =
|
||||
serialize_parameters(invoke_context.transaction_context, instruction_context).unwrap();
|
||||
|
||||
let (de_program_id, de_accounts, de_instruction_data) =
|
||||
unsafe { deserialize_unaligned(&mut serialized.as_slice_mut()[0] as *mut u8) };
|
||||
assert_eq!(&program_id, de_program_id);
|
||||
assert_eq!(instruction_data, de_instruction_data);
|
||||
for account_info in de_accounts {
|
||||
let account = &transaction_accounts
|
||||
.iter()
|
||||
.find(|(key, _account)| key == account_info.key)
|
||||
.unwrap()
|
||||
.1;
|
||||
let index_in_transaction = invoke_context
|
||||
.transaction_context
|
||||
.find_index_of_account(account_info.key)
|
||||
.unwrap();
|
||||
let account = invoke_context
|
||||
.transaction_context
|
||||
.get_account_at_index(index_in_transaction)
|
||||
.borrow();
|
||||
assert_eq!(account.lamports(), account_info.lamports());
|
||||
assert_eq!(account.data(), &account_info.data.borrow()[..]);
|
||||
assert_eq!(account.owner(), account_info.owner);
|
||||
|
@ -557,19 +580,22 @@ mod tests {
|
|||
|
||||
let de_keyed_accounts = invoke_context.get_keyed_accounts().unwrap();
|
||||
deserialize_parameters(
|
||||
&bpf_loader_deprecated::id(),
|
||||
&de_keyed_accounts[1..],
|
||||
invoke_context.transaction_context,
|
||||
instruction_context,
|
||||
serialized.as_slice(),
|
||||
&account_lengths,
|
||||
true,
|
||||
)
|
||||
.unwrap();
|
||||
for keyed_account in de_keyed_accounts {
|
||||
let account = &transaction_accounts
|
||||
.iter()
|
||||
.find(|(key, _account)| key == keyed_account.unsigned_key())
|
||||
.unwrap()
|
||||
.1;
|
||||
let index_in_transaction = invoke_context
|
||||
.transaction_context
|
||||
.find_index_of_account(keyed_account.unsigned_key())
|
||||
.unwrap();
|
||||
let account = invoke_context
|
||||
.transaction_context
|
||||
.get_account_at_index(index_in_transaction)
|
||||
.borrow();
|
||||
assert_eq!(account.lamports(), keyed_account.lamports().unwrap());
|
||||
assert_eq!(
|
||||
account.data(),
|
||||
|
|
|
@ -246,7 +246,8 @@ pub fn bind_syscall_context_objects<'a, 'b>(
|
|||
.is_active(&sol_log_data_syscall_enabled::id());
|
||||
|
||||
let loader_id = invoke_context
|
||||
.get_loader()
|
||||
.transaction_context
|
||||
.get_loader_key()
|
||||
.map_err(SyscallError::InstructionError)?;
|
||||
let invoke_context = Rc::new(RefCell::new(invoke_context));
|
||||
|
||||
|
@ -581,7 +582,8 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallPanic<'a, 'b> {
|
|||
|
||||
let loader_id = question_mark!(
|
||||
invoke_context
|
||||
.get_loader()
|
||||
.transaction_context
|
||||
.get_loader_key()
|
||||
.map_err(SyscallError::InstructionError),
|
||||
result
|
||||
);
|
||||
|
@ -620,7 +622,8 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallLog<'a, 'b> {
|
|||
|
||||
let loader_id = question_mark!(
|
||||
invoke_context
|
||||
.get_loader()
|
||||
.transaction_context
|
||||
.get_loader_key()
|
||||
.map_err(SyscallError::InstructionError),
|
||||
result
|
||||
);
|
||||
|
@ -734,7 +737,8 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallLogPubkey<'a, 'b> {
|
|||
|
||||
let loader_id = question_mark!(
|
||||
invoke_context
|
||||
.get_loader()
|
||||
.transaction_context
|
||||
.get_loader_key()
|
||||
.map_err(SyscallError::InstructionError),
|
||||
result
|
||||
);
|
||||
|
@ -850,7 +854,8 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallCreateProgramAddress<'a, 'b> {
|
|||
|
||||
let loader_id = question_mark!(
|
||||
invoke_context
|
||||
.get_loader()
|
||||
.transaction_context
|
||||
.get_loader_key()
|
||||
.map_err(SyscallError::InstructionError),
|
||||
result
|
||||
);
|
||||
|
@ -909,7 +914,8 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallTryFindProgramAddress<'a, 'b> {
|
|||
|
||||
let loader_id = question_mark!(
|
||||
invoke_context
|
||||
.get_loader()
|
||||
.transaction_context
|
||||
.get_loader_key()
|
||||
.map_err(SyscallError::InstructionError),
|
||||
result
|
||||
);
|
||||
|
@ -983,7 +989,8 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallSha256<'a, 'b> {
|
|||
|
||||
let loader_id = question_mark!(
|
||||
invoke_context
|
||||
.get_loader()
|
||||
.transaction_context
|
||||
.get_loader_key()
|
||||
.map_err(SyscallError::InstructionError),
|
||||
result
|
||||
);
|
||||
|
@ -1064,7 +1071,8 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallGetClockSysvar<'a, 'b> {
|
|||
);
|
||||
let loader_id = question_mark!(
|
||||
invoke_context
|
||||
.get_loader()
|
||||
.transaction_context
|
||||
.get_loader_key()
|
||||
.map_err(SyscallError::InstructionError),
|
||||
result
|
||||
);
|
||||
|
@ -1100,7 +1108,8 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallGetEpochScheduleSysvar<'a, 'b> {
|
|||
);
|
||||
let loader_id = question_mark!(
|
||||
invoke_context
|
||||
.get_loader()
|
||||
.transaction_context
|
||||
.get_loader_key()
|
||||
.map_err(SyscallError::InstructionError),
|
||||
result
|
||||
);
|
||||
|
@ -1137,7 +1146,8 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallGetFeesSysvar<'a, 'b> {
|
|||
);
|
||||
let loader_id = question_mark!(
|
||||
invoke_context
|
||||
.get_loader()
|
||||
.transaction_context
|
||||
.get_loader_key()
|
||||
.map_err(SyscallError::InstructionError),
|
||||
result
|
||||
);
|
||||
|
@ -1173,7 +1183,8 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallGetRentSysvar<'a, 'b> {
|
|||
);
|
||||
let loader_id = question_mark!(
|
||||
invoke_context
|
||||
.get_loader()
|
||||
.transaction_context
|
||||
.get_loader_key()
|
||||
.map_err(SyscallError::InstructionError),
|
||||
result
|
||||
);
|
||||
|
@ -1216,7 +1227,8 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallKeccak256<'a, 'b> {
|
|||
|
||||
let loader_id = question_mark!(
|
||||
invoke_context
|
||||
.get_loader()
|
||||
.transaction_context
|
||||
.get_loader_key()
|
||||
.map_err(SyscallError::InstructionError),
|
||||
result
|
||||
);
|
||||
|
@ -1316,7 +1328,8 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallMemcpy<'a, 'b> {
|
|||
|
||||
let loader_id = question_mark!(
|
||||
invoke_context
|
||||
.get_loader()
|
||||
.transaction_context
|
||||
.get_loader_key()
|
||||
.map_err(SyscallError::InstructionError),
|
||||
result
|
||||
);
|
||||
|
@ -1360,7 +1373,8 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallMemmove<'a, 'b> {
|
|||
|
||||
let loader_id = question_mark!(
|
||||
invoke_context
|
||||
.get_loader()
|
||||
.transaction_context
|
||||
.get_loader_key()
|
||||
.map_err(SyscallError::InstructionError),
|
||||
result
|
||||
);
|
||||
|
@ -1404,7 +1418,8 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallMemcmp<'a, 'b> {
|
|||
|
||||
let loader_id = question_mark!(
|
||||
invoke_context
|
||||
.get_loader()
|
||||
.transaction_context
|
||||
.get_loader_key()
|
||||
.map_err(SyscallError::InstructionError),
|
||||
result
|
||||
);
|
||||
|
@ -1461,7 +1476,8 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallMemset<'a, 'b> {
|
|||
|
||||
let loader_id = question_mark!(
|
||||
invoke_context
|
||||
.get_loader()
|
||||
.transaction_context
|
||||
.get_loader_key()
|
||||
.map_err(SyscallError::InstructionError),
|
||||
result
|
||||
);
|
||||
|
@ -1503,7 +1519,8 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallSecp256k1Recover<'a, 'b> {
|
|||
|
||||
let loader_id = question_mark!(
|
||||
invoke_context
|
||||
.get_loader()
|
||||
.transaction_context
|
||||
.get_loader_key()
|
||||
.map_err(SyscallError::InstructionError),
|
||||
result
|
||||
);
|
||||
|
@ -1608,7 +1625,8 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallBlake3<'a, 'b> {
|
|||
|
||||
let loader_id = question_mark!(
|
||||
invoke_context
|
||||
.get_loader()
|
||||
.transaction_context
|
||||
.get_loader_key()
|
||||
.map_err(SyscallError::InstructionError),
|
||||
result
|
||||
);
|
||||
|
@ -2202,14 +2220,10 @@ fn get_translated_accounts<'a, T, F>(
|
|||
where
|
||||
F: Fn(&T, &InvokeContext) -> Result<CallerAccount<'a>, EbpfError<BpfError>>,
|
||||
{
|
||||
let keyed_accounts = invoke_context
|
||||
.get_keyed_accounts()
|
||||
let instruction_context = invoke_context
|
||||
.transaction_context
|
||||
.get_current_instruction_context()
|
||||
.map_err(SyscallError::InstructionError)?;
|
||||
let number_of_program_accounts = keyed_accounts.len()
|
||||
- invoke_context
|
||||
.get_instruction_keyed_accounts()
|
||||
.map_err(SyscallError::InstructionError)?
|
||||
.len();
|
||||
let mut accounts = Vec::with_capacity(instruction_accounts.len().saturating_add(1));
|
||||
|
||||
let program_account_index = program_indices
|
||||
|
@ -2245,10 +2259,8 @@ where
|
|||
let caller_account = if instruction_account.is_writable {
|
||||
let orig_data_len_index = instruction_account
|
||||
.index_in_caller
|
||||
.saturating_sub(number_of_program_accounts);
|
||||
if keyed_accounts[instruction_account.index_in_caller].unsigned_key() == account_key
|
||||
&& orig_data_len_index < orig_data_lens.len()
|
||||
{
|
||||
.saturating_sub(instruction_context.get_number_of_program_accounts());
|
||||
if orig_data_len_index < orig_data_lens.len() {
|
||||
caller_account.original_data_len = orig_data_lens[orig_data_len_index];
|
||||
} else {
|
||||
ic_msg!(
|
||||
|
@ -2351,7 +2363,8 @@ fn call<'a, 'b: 'a>(
|
|||
|
||||
// Translate and verify caller's data
|
||||
let loader_id = invoke_context
|
||||
.get_loader()
|
||||
.transaction_context
|
||||
.get_loader_key()
|
||||
.map_err(SyscallError::InstructionError)?;
|
||||
let instruction = syscall.translate_instruction(
|
||||
&loader_id,
|
||||
|
@ -2360,7 +2373,8 @@ fn call<'a, 'b: 'a>(
|
|||
*invoke_context,
|
||||
)?;
|
||||
let caller_program_id = invoke_context
|
||||
.get_caller()
|
||||
.transaction_context
|
||||
.get_program_key()
|
||||
.map_err(SyscallError::InstructionError)?;
|
||||
let signers = syscall.translate_signers(
|
||||
&loader_id,
|
||||
|
@ -2369,7 +2383,7 @@ fn call<'a, 'b: 'a>(
|
|||
signers_seeds_len,
|
||||
memory_mapping,
|
||||
)?;
|
||||
let (instruction_accounts, caller_write_privileges, program_indices) = invoke_context
|
||||
let (instruction_accounts, program_indices) = invoke_context
|
||||
.prepare_instruction(&instruction, &signers)
|
||||
.map_err(SyscallError::InstructionError)?;
|
||||
check_authorized_program(&instruction.program_id, &instruction.data, *invoke_context)?;
|
||||
|
@ -2389,7 +2403,6 @@ fn call<'a, 'b: 'a>(
|
|||
.process_instruction(
|
||||
&instruction.data,
|
||||
&instruction_accounts,
|
||||
Some(&caller_write_privileges),
|
||||
&program_indices,
|
||||
&mut compute_units_consumed,
|
||||
)
|
||||
|
@ -2476,7 +2489,8 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallSetReturnData<'a, 'b> {
|
|||
);
|
||||
let loader_id = question_mark!(
|
||||
invoke_context
|
||||
.get_loader()
|
||||
.transaction_context
|
||||
.get_loader_key()
|
||||
.map_err(SyscallError::InstructionError),
|
||||
result
|
||||
);
|
||||
|
@ -2506,7 +2520,8 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallSetReturnData<'a, 'b> {
|
|||
};
|
||||
let program_id = question_mark!(
|
||||
invoke_context
|
||||
.get_caller()
|
||||
.transaction_context
|
||||
.get_program_key()
|
||||
.map_err(SyscallError::InstructionError),
|
||||
result
|
||||
);
|
||||
|
@ -2538,7 +2553,8 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallGetReturnData<'a, 'b> {
|
|||
);
|
||||
let loader_id = question_mark!(
|
||||
invoke_context
|
||||
.get_loader()
|
||||
.transaction_context
|
||||
.get_loader_key()
|
||||
.map_err(SyscallError::InstructionError),
|
||||
result
|
||||
);
|
||||
|
@ -2605,7 +2621,8 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallLogData<'a, 'b> {
|
|||
);
|
||||
let loader_id = question_mark!(
|
||||
invoke_context
|
||||
.get_loader()
|
||||
.transaction_context
|
||||
.get_loader_key()
|
||||
.map_err(SyscallError::InstructionError),
|
||||
result
|
||||
);
|
||||
|
@ -2984,12 +3001,12 @@ mod tests {
|
|||
#[should_panic(expected = "UserError(SyscallError(Panic(\"Gaggablaghblagh!\", 42, 84)))")]
|
||||
fn test_syscall_sol_panic() {
|
||||
let program_id = Pubkey::new_unique();
|
||||
let transaction_context = TransactionContext::new(
|
||||
let mut transaction_context = TransactionContext::new(
|
||||
vec![(program_id, AccountSharedData::new(0, 0, &bpf_loader::id()))],
|
||||
1,
|
||||
);
|
||||
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
|
||||
invoke_context.push(&[], &[0]).unwrap();
|
||||
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
invoke_context.push(&[], &[0], &[]).unwrap();
|
||||
let mut syscall_panic = SyscallPanic {
|
||||
invoke_context: Rc::new(RefCell::new(&mut invoke_context)),
|
||||
};
|
||||
|
@ -3057,12 +3074,12 @@ mod tests {
|
|||
#[test]
|
||||
fn test_syscall_sol_log() {
|
||||
let program_id = Pubkey::new_unique();
|
||||
let transaction_context = TransactionContext::new(
|
||||
let mut transaction_context = TransactionContext::new(
|
||||
vec![(program_id, AccountSharedData::new(0, 0, &bpf_loader::id()))],
|
||||
1,
|
||||
);
|
||||
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
|
||||
invoke_context.push(&[], &[0]).unwrap();
|
||||
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
invoke_context.push(&[], &[0], &[]).unwrap();
|
||||
let mut syscall_sol_log = SyscallLog {
|
||||
invoke_context: Rc::new(RefCell::new(&mut invoke_context)),
|
||||
};
|
||||
|
@ -3157,12 +3174,12 @@ mod tests {
|
|||
#[test]
|
||||
fn test_syscall_sol_log_u64() {
|
||||
let program_id = Pubkey::new_unique();
|
||||
let transaction_context = TransactionContext::new(
|
||||
let mut transaction_context = TransactionContext::new(
|
||||
vec![(program_id, AccountSharedData::new(0, 0, &bpf_loader::id()))],
|
||||
1,
|
||||
);
|
||||
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
|
||||
invoke_context.push(&[], &[0]).unwrap();
|
||||
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
invoke_context.push(&[], &[0], &[]).unwrap();
|
||||
let cost = invoke_context.get_compute_budget().log_64_units;
|
||||
let mut syscall_sol_log_u64 = SyscallLogU64 {
|
||||
invoke_context: Rc::new(RefCell::new(&mut invoke_context)),
|
||||
|
@ -3195,12 +3212,12 @@ mod tests {
|
|||
#[test]
|
||||
fn test_syscall_sol_pubkey() {
|
||||
let program_id = Pubkey::new_unique();
|
||||
let transaction_context = TransactionContext::new(
|
||||
let mut transaction_context = TransactionContext::new(
|
||||
vec![(program_id, AccountSharedData::new(0, 0, &bpf_loader::id()))],
|
||||
1,
|
||||
);
|
||||
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
|
||||
invoke_context.push(&[], &[0]).unwrap();
|
||||
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
invoke_context.push(&[], &[0], &[]).unwrap();
|
||||
let cost = invoke_context.get_compute_budget().log_pubkey_units;
|
||||
let mut syscall_sol_pubkey = SyscallLogPubkey {
|
||||
invoke_context: Rc::new(RefCell::new(&mut invoke_context)),
|
||||
|
@ -3403,15 +3420,15 @@ mod tests {
|
|||
fn test_syscall_sha256() {
|
||||
let config = Config::default();
|
||||
let program_id = Pubkey::new_unique();
|
||||
let transaction_context = TransactionContext::new(
|
||||
let mut transaction_context = TransactionContext::new(
|
||||
vec![(
|
||||
program_id,
|
||||
AccountSharedData::new(0, 0, &bpf_loader_deprecated::id()),
|
||||
)],
|
||||
1,
|
||||
);
|
||||
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
|
||||
invoke_context.push(&[], &[0]).unwrap();
|
||||
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
invoke_context.push(&[], &[0], &[]).unwrap();
|
||||
|
||||
let bytes1 = "Gaggablaghblagh!";
|
||||
let bytes2 = "flurbos";
|
||||
|
@ -3570,13 +3587,13 @@ mod tests {
|
|||
(sysvar::rent::id(), data_rent),
|
||||
];
|
||||
let program_id = Pubkey::new_unique();
|
||||
let transaction_context = TransactionContext::new(
|
||||
let mut transaction_context = TransactionContext::new(
|
||||
vec![(program_id, AccountSharedData::new(0, 0, &bpf_loader::id()))],
|
||||
1,
|
||||
);
|
||||
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
|
||||
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
invoke_context.sysvars = &sysvars;
|
||||
invoke_context.push(&[], &[0]).unwrap();
|
||||
invoke_context.push(&[], &[0], &[]).unwrap();
|
||||
|
||||
// Test clock sysvar
|
||||
{
|
||||
|
@ -3820,12 +3837,12 @@ mod tests {
|
|||
// These tests duplicate the direct tests in solana_program::pubkey
|
||||
|
||||
let program_id = Pubkey::new_unique();
|
||||
let transaction_context = TransactionContext::new(
|
||||
let mut transaction_context = TransactionContext::new(
|
||||
vec![(program_id, AccountSharedData::new(0, 0, &bpf_loader::id()))],
|
||||
1,
|
||||
);
|
||||
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
|
||||
invoke_context.push(&[], &[0]).unwrap();
|
||||
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
invoke_context.push(&[], &[0], &[]).unwrap();
|
||||
let address = bpf_loader_upgradeable::id();
|
||||
|
||||
let exceeded_seed = &[127; MAX_SEED_LEN + 1];
|
||||
|
@ -3932,12 +3949,12 @@ mod tests {
|
|||
#[test]
|
||||
fn test_find_program_address() {
|
||||
let program_id = Pubkey::new_unique();
|
||||
let transaction_context = TransactionContext::new(
|
||||
let mut transaction_context = TransactionContext::new(
|
||||
vec![(program_id, AccountSharedData::new(0, 0, &bpf_loader::id()))],
|
||||
1,
|
||||
);
|
||||
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
|
||||
invoke_context.push(&[], &[0]).unwrap();
|
||||
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
invoke_context.push(&[], &[0], &[]).unwrap();
|
||||
let cost = invoke_context
|
||||
.get_compute_budget()
|
||||
.create_program_address_units;
|
||||
|
|
|
@ -4999,8 +4999,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_merge() {
|
||||
let transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
|
||||
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
let stake_pubkey = solana_sdk::pubkey::new_rand();
|
||||
let source_stake_pubkey = solana_sdk::pubkey::new_rand();
|
||||
let authorized_pubkey = solana_sdk::pubkey::new_rand();
|
||||
|
@ -5110,8 +5110,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_merge_self_fails() {
|
||||
let transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
|
||||
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
let stake_address = Pubkey::new_unique();
|
||||
let authority_pubkey = Pubkey::new_unique();
|
||||
let signers = HashSet::from_iter(vec![authority_pubkey]);
|
||||
|
@ -5156,8 +5156,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_merge_incorrect_authorized_staker() {
|
||||
let transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
|
||||
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
let stake_pubkey = solana_sdk::pubkey::new_rand();
|
||||
let source_stake_pubkey = solana_sdk::pubkey::new_rand();
|
||||
let authorized_pubkey = solana_sdk::pubkey::new_rand();
|
||||
|
@ -5226,8 +5226,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_merge_invalid_account_data() {
|
||||
let transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
|
||||
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
let stake_pubkey = solana_sdk::pubkey::new_rand();
|
||||
let source_stake_pubkey = solana_sdk::pubkey::new_rand();
|
||||
let authorized_pubkey = solana_sdk::pubkey::new_rand();
|
||||
|
@ -5277,8 +5277,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_merge_fake_stake_source() {
|
||||
let transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
|
||||
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
let stake_pubkey = solana_sdk::pubkey::new_rand();
|
||||
let source_stake_pubkey = solana_sdk::pubkey::new_rand();
|
||||
let authorized_pubkey = solana_sdk::pubkey::new_rand();
|
||||
|
@ -5320,8 +5320,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_merge_active_stake() {
|
||||
let transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
|
||||
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
let base_lamports = 4242424242;
|
||||
let stake_address = Pubkey::new_unique();
|
||||
let source_address = Pubkey::new_unique();
|
||||
|
@ -5943,8 +5943,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_things_can_merge() {
|
||||
let transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
|
||||
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
let good_stake = Stake {
|
||||
credits_observed: 4242,
|
||||
delegation: Delegation {
|
||||
|
@ -6042,8 +6042,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_metas_can_merge_pre_v4() {
|
||||
let transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
|
||||
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
// Identical Metas can merge
|
||||
assert!(MergeKind::metas_can_merge(
|
||||
&invoke_context,
|
||||
|
@ -6129,8 +6129,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_metas_can_merge_v4() {
|
||||
let transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
|
||||
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
// Identical Metas can merge
|
||||
assert!(MergeKind::metas_can_merge(
|
||||
&invoke_context,
|
||||
|
@ -6276,8 +6276,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_merge_kind_get_if_mergeable() {
|
||||
let transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
|
||||
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
let authority_pubkey = Pubkey::new_unique();
|
||||
let initial_lamports = 4242424242;
|
||||
let rent = Rent::default();
|
||||
|
@ -6509,8 +6509,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_merge_kind_merge() {
|
||||
let transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
|
||||
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
let lamports = 424242;
|
||||
let meta = Meta {
|
||||
rent_exempt_reserve: 42,
|
||||
|
@ -6588,8 +6588,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_active_stake_merge() {
|
||||
let transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
|
||||
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
let delegation_a = 4_242_424_242u64;
|
||||
let delegation_b = 6_200_000_000u64;
|
||||
let credits_a = 124_521_000u64;
|
||||
|
|
|
@ -212,17 +212,21 @@ native machine code before execting it in the virtual machine.",
|
|||
let program_indices = [0, 1];
|
||||
let preparation =
|
||||
prepare_mock_invoke_context(transaction_accounts, instruction_accounts, &program_indices);
|
||||
let transaction_context = TransactionContext::new(preparation.transaction_accounts, 1);
|
||||
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
|
||||
let mut transaction_context = TransactionContext::new(preparation.transaction_accounts, 1);
|
||||
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
invoke_context
|
||||
.push(&preparation.instruction_accounts, &program_indices)
|
||||
.push(
|
||||
&preparation.instruction_accounts,
|
||||
&program_indices,
|
||||
&instruction_data,
|
||||
)
|
||||
.unwrap();
|
||||
let keyed_accounts = invoke_context.get_keyed_accounts().unwrap();
|
||||
let (mut parameter_bytes, account_lengths) = serialize_parameters(
|
||||
keyed_accounts[0].unsigned_key(),
|
||||
keyed_accounts[1].unsigned_key(),
|
||||
&keyed_accounts[2..],
|
||||
&instruction_data,
|
||||
invoke_context.transaction_context,
|
||||
invoke_context
|
||||
.transaction_context
|
||||
.get_current_instruction_context()
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
let compute_meter = invoke_context.get_compute_meter();
|
||||
|
|
|
@ -3561,9 +3561,9 @@ impl Bank {
|
|||
|
||||
let mut transaction_accounts = Vec::new();
|
||||
std::mem::swap(&mut loaded_transaction.accounts, &mut transaction_accounts);
|
||||
let transaction_context = TransactionContext::new(
|
||||
let mut transaction_context = TransactionContext::new(
|
||||
transaction_accounts,
|
||||
compute_budget.max_invoke_depth,
|
||||
compute_budget.max_invoke_depth.saturating_add(1),
|
||||
);
|
||||
|
||||
let instruction_recorder = if enable_cpi_recording {
|
||||
|
@ -3588,7 +3588,7 @@ impl Bank {
|
|||
&self.builtin_programs.vec,
|
||||
legacy_message,
|
||||
&loaded_transaction.program_indices,
|
||||
&transaction_context,
|
||||
&mut transaction_context,
|
||||
self.rent_collector.rent,
|
||||
log_collector.clone(),
|
||||
executors.clone(),
|
||||
|
@ -10401,7 +10401,7 @@ pub(crate) mod tests {
|
|||
_instruction_data: &[u8],
|
||||
invoke_context: &mut InvokeContext,
|
||||
) -> std::result::Result<(), InstructionError> {
|
||||
let program_id = invoke_context.get_caller()?;
|
||||
let program_id = invoke_context.transaction_context.get_program_key()?;
|
||||
if mock_vote_program_id() != *program_id {
|
||||
return Err(InstructionError::IncorrectProgramId);
|
||||
}
|
||||
|
|
|
@ -19,12 +19,12 @@ fn process_instruction_with_program_logging(
|
|||
invoke_context: &mut InvokeContext,
|
||||
) -> Result<(), InstructionError> {
|
||||
let logger = invoke_context.get_log_collector();
|
||||
let program_id = invoke_context.get_caller()?;
|
||||
let program_id = invoke_context.transaction_context.get_program_key()?;
|
||||
stable_log::program_invoke(&logger, program_id, invoke_context.invoke_depth());
|
||||
|
||||
let result = process_instruction(first_instruction_account, instruction_data, invoke_context);
|
||||
|
||||
let program_id = invoke_context.get_caller()?;
|
||||
let program_id = invoke_context.transaction_context.get_program_key()?;
|
||||
match &result {
|
||||
Ok(()) => stable_log::program_success(&logger, program_id),
|
||||
Err(err) => stable_log::program_failure(&logger, program_id, err),
|
||||
|
|
|
@ -53,7 +53,7 @@ impl MessageProcessor {
|
|||
builtin_programs: &[BuiltinProgram],
|
||||
message: &Message,
|
||||
program_indices: &[Vec<usize>],
|
||||
transaction_context: &TransactionContext,
|
||||
transaction_context: &mut TransactionContext,
|
||||
rent: Rent,
|
||||
log_collector: Option<Rc<RefCell<LogCollector>>>,
|
||||
executors: Rc<RefCell<Executors>>,
|
||||
|
@ -132,7 +132,6 @@ impl MessageProcessor {
|
|||
let result = invoke_context.process_instruction(
|
||||
&instruction.data,
|
||||
&instruction_accounts,
|
||||
None,
|
||||
program_indices,
|
||||
&mut compute_units_consumed,
|
||||
);
|
||||
|
@ -242,7 +241,7 @@ mod tests {
|
|||
create_loadable_account_for_test("mock_system_program"),
|
||||
),
|
||||
];
|
||||
let transaction_context = TransactionContext::new(accounts, 1);
|
||||
let mut transaction_context = TransactionContext::new(accounts, 1);
|
||||
let program_indices = vec![vec![2]];
|
||||
let executors = Rc::new(RefCell::new(Executors::default()));
|
||||
let account_metas = vec![
|
||||
|
@ -262,7 +261,7 @@ mod tests {
|
|||
builtin_programs,
|
||||
&message,
|
||||
&program_indices,
|
||||
&transaction_context,
|
||||
&mut transaction_context,
|
||||
rent_collector.rent,
|
||||
None,
|
||||
executors.clone(),
|
||||
|
@ -303,7 +302,7 @@ mod tests {
|
|||
builtin_programs,
|
||||
&message,
|
||||
&program_indices,
|
||||
&transaction_context,
|
||||
&mut transaction_context,
|
||||
rent_collector.rent,
|
||||
None,
|
||||
executors.clone(),
|
||||
|
@ -336,7 +335,7 @@ mod tests {
|
|||
builtin_programs,
|
||||
&message,
|
||||
&program_indices,
|
||||
&transaction_context,
|
||||
&mut transaction_context,
|
||||
rent_collector.rent,
|
||||
None,
|
||||
executors,
|
||||
|
@ -458,7 +457,7 @@ mod tests {
|
|||
create_loadable_account_for_test("mock_system_program"),
|
||||
),
|
||||
];
|
||||
let transaction_context = TransactionContext::new(accounts, 1);
|
||||
let mut transaction_context = TransactionContext::new(accounts, 1);
|
||||
let program_indices = vec![vec![2]];
|
||||
let executors = Rc::new(RefCell::new(Executors::default()));
|
||||
let account_metas = vec![
|
||||
|
@ -480,7 +479,7 @@ mod tests {
|
|||
builtin_programs,
|
||||
&message,
|
||||
&program_indices,
|
||||
&transaction_context,
|
||||
&mut transaction_context,
|
||||
rent_collector.rent,
|
||||
None,
|
||||
executors.clone(),
|
||||
|
@ -514,7 +513,7 @@ mod tests {
|
|||
builtin_programs,
|
||||
&message,
|
||||
&program_indices,
|
||||
&transaction_context,
|
||||
&mut transaction_context,
|
||||
rent_collector.rent,
|
||||
None,
|
||||
executors.clone(),
|
||||
|
@ -545,7 +544,7 @@ mod tests {
|
|||
builtin_programs,
|
||||
&message,
|
||||
&program_indices,
|
||||
&transaction_context,
|
||||
&mut transaction_context,
|
||||
rent_collector.rent,
|
||||
None,
|
||||
executors,
|
||||
|
@ -602,7 +601,7 @@ mod tests {
|
|||
(secp256k1_program::id(), secp256k1_account),
|
||||
(mock_program_id, mock_program_account),
|
||||
];
|
||||
let transaction_context = TransactionContext::new(accounts, 1);
|
||||
let mut transaction_context = TransactionContext::new(accounts, 1);
|
||||
|
||||
let message = Message::new(
|
||||
&[
|
||||
|
@ -618,7 +617,7 @@ mod tests {
|
|||
builtin_programs,
|
||||
&message,
|
||||
&[vec![0], vec![1]],
|
||||
&transaction_context,
|
||||
&mut transaction_context,
|
||||
RentCollector::default().rent,
|
||||
None,
|
||||
Rc::new(RefCell::new(Executors::default())),
|
||||
|
|
|
@ -334,8 +334,8 @@ mod test {
|
|||
where
|
||||
F: FnMut(&mut InvokeContext, &KeyedAccount),
|
||||
{
|
||||
let transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
|
||||
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
let pubkey = Pubkey::new_unique();
|
||||
let account = create_account(lamports);
|
||||
let keyed_account = KeyedAccount::new(&pubkey, signer, &account);
|
||||
|
|
|
@ -675,8 +675,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_address_create_with_seed_mismatch() {
|
||||
let transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
|
||||
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
let from = Pubkey::new_unique();
|
||||
let seed = "dull boy";
|
||||
let to = Pubkey::new_unique();
|
||||
|
@ -690,8 +690,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_create_account_with_seed_missing_sig() {
|
||||
let transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
|
||||
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
let new_owner = Pubkey::new(&[9; 32]);
|
||||
let from = Pubkey::new_unique();
|
||||
let seed = "dull boy";
|
||||
|
@ -721,8 +721,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_create_with_zero_lamports() {
|
||||
let transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
|
||||
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
// create account with zero lamports transferred
|
||||
let new_owner = Pubkey::new(&[9; 32]);
|
||||
let from = Pubkey::new_unique();
|
||||
|
@ -756,8 +756,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_create_negative_lamports() {
|
||||
let transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
|
||||
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
// Attempt to create account with more lamports than remaining in from_account
|
||||
let new_owner = Pubkey::new(&[9; 32]);
|
||||
let from = Pubkey::new_unique();
|
||||
|
@ -781,8 +781,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_request_more_than_allowed_data_length() {
|
||||
let transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
|
||||
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
let from_account = RefCell::new(AccountSharedData::new(100, 0, &system_program::id()));
|
||||
let from = Pubkey::new_unique();
|
||||
let to_account = RefCell::new(AccountSharedData::new(0, 0, &system_program::id()));
|
||||
|
@ -829,8 +829,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_create_already_in_use() {
|
||||
let transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
|
||||
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
// Attempt to create system account in account already owned by another program
|
||||
let new_owner = Pubkey::new(&[9; 32]);
|
||||
let from = Pubkey::new_unique();
|
||||
|
@ -898,8 +898,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_create_unsigned() {
|
||||
let transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
|
||||
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
// Attempt to create an account without signing the transfer
|
||||
let new_owner = Pubkey::new(&[9; 32]);
|
||||
let from = Pubkey::new_unique();
|
||||
|
@ -954,8 +954,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_create_sysvar_invalid_id_with_feature() {
|
||||
let transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
|
||||
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
// Attempt to create system account in account already owned by another program
|
||||
let from = Pubkey::new_unique();
|
||||
let from_account = RefCell::new(AccountSharedData::new(100, 0, &system_program::id()));
|
||||
|
@ -989,8 +989,8 @@ mod tests {
|
|||
feature_set
|
||||
.inactive
|
||||
.insert(feature_set::rent_for_sysvars::id());
|
||||
let transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
|
||||
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
invoke_context.feature_set = Arc::new(feature_set);
|
||||
// Attempt to create system account in account already owned by another program
|
||||
let from = Pubkey::new_unique();
|
||||
|
@ -1017,8 +1017,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_create_data_populated() {
|
||||
let transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
|
||||
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
// Attempt to create system account in account with populated data
|
||||
let new_owner = Pubkey::new(&[9; 32]);
|
||||
let from = Pubkey::new_unique();
|
||||
|
@ -1051,8 +1051,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_create_from_account_is_nonce_fail() {
|
||||
let transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
|
||||
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
let nonce = Pubkey::new_unique();
|
||||
let nonce_account = RefCell::new(
|
||||
AccountSharedData::new_data(
|
||||
|
@ -1090,8 +1090,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_assign() {
|
||||
let transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
|
||||
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
let new_owner = Pubkey::new(&[9; 32]);
|
||||
let pubkey = Pubkey::new_unique();
|
||||
let mut account = AccountSharedData::new(100, 0, &system_program::id());
|
||||
|
@ -1133,8 +1133,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_assign_to_sysvar_with_feature() {
|
||||
let transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
|
||||
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
let new_owner = sysvar::id();
|
||||
let from = Pubkey::new_unique();
|
||||
let mut from_account = AccountSharedData::new(100, 0, &system_program::id());
|
||||
|
@ -1160,8 +1160,8 @@ mod tests {
|
|||
feature_set
|
||||
.inactive
|
||||
.insert(feature_set::rent_for_sysvars::id());
|
||||
let transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
|
||||
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
invoke_context.feature_set = Arc::new(feature_set);
|
||||
let new_owner = sysvar::id();
|
||||
let from = Pubkey::new_unique();
|
||||
|
@ -1212,8 +1212,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_transfer_lamports() {
|
||||
let transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
|
||||
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
let from = Pubkey::new_unique();
|
||||
let from_account = RefCell::new(AccountSharedData::new(100, 0, &Pubkey::new(&[2; 32]))); // account owner should not matter
|
||||
let to = Pubkey::new(&[3; 32]);
|
||||
|
@ -1251,8 +1251,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_transfer_with_seed() {
|
||||
let transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
|
||||
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
let base = Pubkey::new_unique();
|
||||
let base_account = RefCell::new(AccountSharedData::new(100, 0, &Pubkey::new(&[2; 32]))); // account owner should not matter
|
||||
let from_base_keyed_account = KeyedAccount::new(&base, true, &base_account);
|
||||
|
@ -1312,8 +1312,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_transfer_lamports_from_nonce_account_fail() {
|
||||
let transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
|
||||
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
|
||||
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
let from = Pubkey::new_unique();
|
||||
let from_account = RefCell::new(
|
||||
AccountSharedData::new_data(
|
||||
|
|
|
@ -6,7 +6,10 @@ use crate::{
|
|||
lamports::LamportsError,
|
||||
pubkey::Pubkey,
|
||||
};
|
||||
use std::cell::{RefCell, RefMut};
|
||||
use std::{
|
||||
cell::{RefCell, RefMut},
|
||||
pin::Pin,
|
||||
};
|
||||
|
||||
pub type TransactionAccount = (Pubkey, AccountSharedData);
|
||||
|
||||
|
@ -23,8 +26,8 @@ pub struct InstructionAccount {
|
|||
/// This context is valid for the entire duration of a transaction being processed.
|
||||
#[derive(Debug)]
|
||||
pub struct TransactionContext {
|
||||
account_keys: Vec<Pubkey>,
|
||||
accounts: Vec<RefCell<AccountSharedData>>,
|
||||
account_keys: Pin<Box<[Pubkey]>>,
|
||||
accounts: Pin<Box<[RefCell<AccountSharedData>]>>,
|
||||
instruction_context_capacity: usize,
|
||||
instruction_context_stack: Vec<InstructionContext>,
|
||||
return_data: (Pubkey, Vec<u8>),
|
||||
|
@ -36,13 +39,14 @@ impl TransactionContext {
|
|||
transaction_accounts: Vec<TransactionAccount>,
|
||||
instruction_context_capacity: usize,
|
||||
) -> Self {
|
||||
let (account_keys, accounts) = transaction_accounts
|
||||
.into_iter()
|
||||
.map(|(key, account)| (key, RefCell::new(account)))
|
||||
.unzip();
|
||||
let (account_keys, accounts): (Vec<Pubkey>, Vec<RefCell<AccountSharedData>>) =
|
||||
transaction_accounts
|
||||
.into_iter()
|
||||
.map(|(key, account)| (key, RefCell::new(account)))
|
||||
.unzip();
|
||||
Self {
|
||||
account_keys,
|
||||
accounts,
|
||||
account_keys: Pin::new(account_keys.into_boxed_slice()),
|
||||
accounts: Pin::new(accounts.into_boxed_slice()),
|
||||
instruction_context_capacity,
|
||||
instruction_context_stack: Vec::with_capacity(instruction_context_capacity),
|
||||
return_data: (Pubkey::default(), Vec::new()),
|
||||
|
@ -51,10 +55,10 @@ impl TransactionContext {
|
|||
|
||||
/// Used by the bank in the runtime to write back the processed accounts
|
||||
pub fn deconstruct(self) -> Vec<TransactionAccount> {
|
||||
self.account_keys
|
||||
Vec::from(Pin::into_inner(self.account_keys))
|
||||
.into_iter()
|
||||
.zip(
|
||||
self.accounts
|
||||
Vec::from(Pin::into_inner(self.accounts))
|
||||
.into_iter()
|
||||
.map(|account| account.into_inner()),
|
||||
)
|
||||
|
@ -66,8 +70,7 @@ impl TransactionContext {
|
|||
if !self.instruction_context_stack.is_empty() {
|
||||
return Err(InstructionError::CallDepth);
|
||||
}
|
||||
Ok(self
|
||||
.accounts
|
||||
Ok(Vec::from(Pin::into_inner(self.accounts))
|
||||
.into_iter()
|
||||
.map(|account| account.into_inner())
|
||||
.collect())
|
||||
|
@ -243,21 +246,29 @@ impl InstructionContext {
|
|||
.map(|index| index.saturating_add(self.program_accounts.len()))
|
||||
}
|
||||
|
||||
/// Translates the given instruction wide index into a transaction wide index
|
||||
pub fn get_index_in_transaction(
|
||||
&self,
|
||||
index_in_instruction: usize,
|
||||
) -> Result<usize, InstructionError> {
|
||||
if index_in_instruction < self.program_accounts.len() {
|
||||
Ok(self.program_accounts[index_in_instruction])
|
||||
} else if index_in_instruction < self.get_number_of_accounts() {
|
||||
Ok(self.instruction_accounts
|
||||
[index_in_instruction.saturating_sub(self.program_accounts.len())]
|
||||
.index_in_transaction)
|
||||
} else {
|
||||
Err(InstructionError::NotEnoughAccountKeys)
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to borrow an account from this Instruction
|
||||
pub fn try_borrow_account<'a, 'b: 'a>(
|
||||
&'a self,
|
||||
transaction_context: &'b TransactionContext,
|
||||
index_in_instruction: usize,
|
||||
) -> Result<BorrowedAccount<'a>, InstructionError> {
|
||||
let index_in_transaction = if index_in_instruction < self.program_accounts.len() {
|
||||
self.program_accounts[index_in_instruction]
|
||||
} else if index_in_instruction < self.get_number_of_accounts() {
|
||||
self.instruction_accounts
|
||||
[index_in_instruction.saturating_sub(self.program_accounts.len())]
|
||||
.index_in_transaction
|
||||
} else {
|
||||
return Err(InstructionError::NotEnoughAccountKeys);
|
||||
};
|
||||
let index_in_transaction = self.get_index_in_transaction(index_in_instruction)?;
|
||||
if index_in_transaction >= transaction_context.accounts.len() {
|
||||
return Err(InstructionError::MissingAccount);
|
||||
}
|
||||
|
@ -331,12 +342,13 @@ impl<'a> BorrowedAccount<'a> {
|
|||
}
|
||||
|
||||
/// Assignes the owner of this account (transaction wide)
|
||||
pub fn set_owner(&mut self, pubkey: Pubkey) -> Result<(), InstructionError> {
|
||||
if !self.is_writable() {
|
||||
return Err(InstructionError::Immutable);
|
||||
pub fn set_owner(&mut self, pubkey: &[u8]) -> Result<(), InstructionError> {
|
||||
self.account.copy_into_owner_from_slice(pubkey);
|
||||
if self.is_writable() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(InstructionError::Immutable)
|
||||
}
|
||||
self.account.set_owner(pubkey);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the number of lamports of this account (transaction wide)
|
||||
|
@ -346,11 +358,12 @@ impl<'a> BorrowedAccount<'a> {
|
|||
|
||||
/// Overwrites the number of lamports of this account (transaction wide)
|
||||
pub fn set_lamports(&mut self, lamports: u64) -> Result<(), InstructionError> {
|
||||
if !self.is_writable() {
|
||||
return Err(InstructionError::Immutable);
|
||||
}
|
||||
self.account.set_lamports(lamports);
|
||||
Ok(())
|
||||
if self.is_writable() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(InstructionError::Immutable)
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds lamports to this account (transaction wide)
|
||||
|
@ -376,26 +389,23 @@ impl<'a> BorrowedAccount<'a> {
|
|||
self.account.data()
|
||||
}
|
||||
|
||||
/// Returns a writable slice of the account data (transaction wide)
|
||||
pub fn get_data_mut(&mut self) -> Result<&mut [u8], InstructionError> {
|
||||
if !self.is_writable() {
|
||||
return Err(InstructionError::Immutable);
|
||||
/// Overwrites the account data and size (transaction wide)
|
||||
pub fn set_data(&mut self, data: &[u8]) -> Result<(), InstructionError> {
|
||||
if data.len() == self.account.data().len() {
|
||||
self.account.data_as_mut_slice().copy_from_slice(data);
|
||||
} else {
|
||||
self.account.set_data_from_slice(data);
|
||||
}
|
||||
if self.is_writable() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(InstructionError::Immutable)
|
||||
}
|
||||
Ok(self.account.data_as_mut_slice())
|
||||
}
|
||||
|
||||
/// Guarded alternative to `get_data_mut()?.copy_from_slice()` which checks if the account size matches
|
||||
pub fn copy_from_slice(&mut self, data: &[u8]) -> Result<(), InstructionError> {
|
||||
if !self.is_writable() {
|
||||
return Err(InstructionError::Immutable);
|
||||
}
|
||||
let account_data = self.account.data_as_mut_slice();
|
||||
if data.len() != account_data.len() {
|
||||
return Err(InstructionError::AccountDataSizeChanged);
|
||||
}
|
||||
account_data.copy_from_slice(data);
|
||||
Ok(())
|
||||
}
|
||||
/*pub fn realloc(&self, new_len: usize, zero_init: bool) {
|
||||
// TODO
|
||||
}*/
|
||||
|
||||
/// Deserializes the account data into a state
|
||||
pub fn get_state<T: serde::de::DeserializeOwned>(&self) -> Result<T, InstructionError> {
|
||||
|
@ -406,22 +416,20 @@ impl<'a> BorrowedAccount<'a> {
|
|||
|
||||
/// Serializes a state into the account data
|
||||
pub fn set_state<T: serde::Serialize>(&mut self, state: &T) -> Result<(), InstructionError> {
|
||||
if !self.is_writable() {
|
||||
return Err(InstructionError::Immutable);
|
||||
}
|
||||
let data = self.account.data_as_mut_slice();
|
||||
let serialized_size =
|
||||
bincode::serialized_size(state).map_err(|_| InstructionError::GenericError)?;
|
||||
if serialized_size > data.len() as u64 {
|
||||
return Err(InstructionError::AccountDataTooSmall);
|
||||
}
|
||||
bincode::serialize_into(&mut *data, state).map_err(|_| InstructionError::GenericError)
|
||||
bincode::serialize_into(&mut *data, state).map_err(|_| InstructionError::GenericError)?;
|
||||
if self.is_writable() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(InstructionError::Immutable)
|
||||
}
|
||||
}
|
||||
|
||||
/*pub fn realloc(&self, new_len: usize, zero_init: bool) {
|
||||
// TODO
|
||||
}*/
|
||||
|
||||
/// Returns whether this account is executable (transaction wide)
|
||||
pub fn is_executable(&self) -> bool {
|
||||
self.account.executable()
|
||||
|
@ -429,11 +437,17 @@ impl<'a> BorrowedAccount<'a> {
|
|||
|
||||
/// Configures whether this account is executable (transaction wide)
|
||||
pub fn set_executable(&mut self, is_executable: bool) -> Result<(), InstructionError> {
|
||||
if !self.is_writable() {
|
||||
return Err(InstructionError::Immutable);
|
||||
}
|
||||
self.account.set_executable(is_executable);
|
||||
Ok(())
|
||||
if self.is_writable() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(InstructionError::Immutable)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the rent epoch of this account (transaction wide)
|
||||
pub fn get_rent_epoch(&self) -> u64 {
|
||||
self.account.rent_epoch()
|
||||
}
|
||||
|
||||
/// Returns whether this account is a signer (instruction wide)
|
||||
|
|
Loading…
Reference in New Issue