Unifies ThisInvokeContext and dyn trait InvokeContext. (#21563)

This commit is contained in:
Alexander Meißner
2021-12-02 18:47:16 +01:00
committed by GitHub
parent 8dfa83c579
commit bfdb775ffc
21 changed files with 366 additions and 431 deletions

View File

@ -22,7 +22,7 @@ use solana_sdk::{
use std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc, sync::Arc};
pub type ProcessInstructionWithContext =
fn(usize, &[u8], &mut dyn InvokeContext) -> Result<(), InstructionError>;
fn(usize, &[u8], &mut InvokeContext) -> Result<(), InstructionError>;
#[derive(Clone)]
pub struct BuiltinProgram {
@ -36,7 +36,7 @@ impl std::fmt::Debug for BuiltinProgram {
type ErasedProcessInstructionWithContext = fn(
usize,
&'static [u8],
&'static mut dyn InvokeContext,
&'static mut InvokeContext<'static>,
) -> Result<(), InstructionError>;
// rustc doesn't compile due to bug without this work around
@ -50,11 +50,11 @@ impl std::fmt::Debug for BuiltinProgram {
/// Program executor
pub trait Executor: Debug + Send + Sync {
/// Execute the program
fn execute(
fn execute<'a, 'b>(
&self,
first_instruction_account: usize,
instruction_data: &[u8],
invoke_context: &mut dyn InvokeContext,
invoke_context: &'a mut InvokeContext<'b>,
use_jit: bool,
) -> Result<(), InstructionError>;
}
@ -130,7 +130,7 @@ impl<'a> StackFrame<'a> {
}
}
pub struct ThisInvokeContext<'a> {
pub struct InvokeContext<'a> {
instruction_index: usize,
invoke_stack: Vec<StackFrame<'a>>,
rent: Rent,
@ -150,7 +150,8 @@ pub struct ThisInvokeContext<'a> {
lamports_per_signature: u64,
return_data: (Pubkey, Vec<u8>),
}
impl<'a> ThisInvokeContext<'a> {
impl<'a> InvokeContext<'a> {
#[allow(clippy::too_many_arguments)]
pub fn new(
rent: Rent,
@ -221,117 +222,9 @@ impl<'a> ThisInvokeContext<'a> {
Arc::new(FeatureSet::all_enabled()),
)
}
}
/// Invocation context passed to loaders
pub trait InvokeContext {
/// Push a stack frame onto the invocation stack
fn push(
&mut self,
message: &Message,
instruction: &CompiledInstruction,
program_indices: &[usize],
account_indices: Option<&[usize]>,
) -> Result<(), InstructionError>;
/// Pop a stack frame from the invocation stack
fn pop(&mut self);
/// Current depth of the invocation stake
fn invoke_depth(&self) -> usize;
/// Verify the results of an instruction
fn verify(
&mut self,
message: &Message,
instruction: &CompiledInstruction,
program_indices: &[usize],
) -> Result<(), InstructionError>;
/// Verify and update PreAccount state based on program execution
fn verify_and_update(
&mut self,
instruction: &CompiledInstruction,
account_indices: &[usize],
write_privileges: &[bool],
) -> Result<(), InstructionError>;
/// Entrypoint for a cross-program invocation from a native program
fn native_invoke(
&mut self,
instruction: Instruction,
signers: &[Pubkey],
) -> Result<(), InstructionError>;
/// Helper to prepare for process_cross_program_instruction()
fn create_message(
&mut self,
instruction: &Instruction,
signers: &[Pubkey],
) -> Result<(Message, Vec<bool>, Vec<usize>), InstructionError>;
/// Process a cross-program instruction
fn process_cross_program_instruction(
&mut self,
message: &Message,
program_indices: &[usize],
account_indices: &[usize],
caller_write_privileges: &[bool],
) -> Result<(), InstructionError>;
/// Calls the instruction's program entrypoint method
fn process_instruction(&mut self, instruction_data: &[u8]) -> Result<(), InstructionError>;
/// Get the program ID of the currently executing program
fn get_caller(&self) -> Result<&Pubkey, InstructionError>;
/// Get the owner of the currently executing program
fn get_loader(&self) -> Result<Pubkey, InstructionError>;
/// Removes the first keyed account
#[deprecated(
since = "1.9.0",
note = "To be removed together with remove_native_loader"
)]
fn remove_first_keyed_account(&mut self) -> Result<(), InstructionError>;
/// Get the list of keyed accounts
fn get_keyed_accounts(&self) -> Result<&[KeyedAccount], InstructionError>;
/// Get the list of keyed accounts skipping `first_instruction_account` many entries
fn get_instruction_keyed_accounts(&self) -> Result<&[KeyedAccount], InstructionError>;
/// Get this invocation's LogCollector
fn get_log_collector(&self) -> Option<Rc<RefCell<LogCollector>>>;
/// Get this invocation's ComputeMeter
fn get_compute_meter(&self) -> Rc<RefCell<ComputeMeter>>;
/// Loaders may need to do work in order to execute a program. Cache
/// the work that can be re-used across executions
fn add_executor(&self, pubkey: &Pubkey, executor: Arc<dyn Executor>);
/// Get the completed loader work that can be re-used across executions
fn get_executor(&self, pubkey: &Pubkey) -> Option<Arc<dyn Executor>>;
/// Set which instruction in the message is currently being recorded
fn set_instruction_index(&mut self, instruction_index: usize);
/// Record invoked instruction
fn record_instruction(&self, instruction: &Instruction);
/// Get the bank's active feature set
fn is_feature_active(&self, feature_id: &Pubkey) -> bool;
/// Find an account_index and account by its key
fn get_account(&self, pubkey: &Pubkey) -> Option<(usize, Rc<RefCell<AccountSharedData>>)>;
/// Update timing
fn update_timing(
&mut self,
serialize_us: u64,
create_vm_us: u64,
execute_us: u64,
deserialize_us: u64,
);
/// Get sysvars
fn get_sysvars(&self) -> &[(Pubkey, Vec<u8>)];
/// Get this invocation's compute budget
fn get_compute_budget(&self) -> &ComputeBudget;
/// Set this invocation's blockhash
fn set_blockhash(&mut self, hash: Hash);
/// Get this invocation's blockhash
fn get_blockhash(&self) -> &Hash;
/// Set this invocation's lamports_per_signature value
fn set_lamports_per_signature(&mut self, lamports_per_signature: u64);
/// Get this invocation's lamports_per_signature value
fn get_lamports_per_signature(&self) -> u64;
/// Set the return data
fn set_return_data(&mut self, data: Vec<u8>) -> Result<(), InstructionError>;
/// Get the return data
fn get_return_data(&self) -> (Pubkey, &[u8]);
}
impl<'a> InvokeContext for ThisInvokeContext<'a> {
fn push(
pub fn push(
&mut self,
message: &Message,
instruction: &CompiledInstruction,
@ -430,13 +323,19 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> {
));
Ok(())
}
fn pop(&mut self) {
/// Pop a stack frame from the invocation stack
pub fn pop(&mut self) {
self.invoke_stack.pop();
}
fn invoke_depth(&self) -> usize {
/// Current depth of the invocation stack
pub fn invoke_depth(&self) -> usize {
self.invoke_stack.len()
}
fn verify(
/// Verify the results of an instruction
pub fn verify(
&mut self,
message: &Message,
instruction: &CompiledInstruction,
@ -501,7 +400,9 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> {
}
Ok(())
}
fn verify_and_update(
/// Verify and update PreAccount state based on program execution
pub fn verify_and_update(
&mut self,
instruction: &CompiledInstruction,
account_indices: &[usize],
@ -580,7 +481,9 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> {
}
Ok(())
}
fn native_invoke(
/// Entrypoint for a cross-program invocation from a builtin program
pub fn native_invoke(
&mut self,
instruction: Instruction,
signers: &[Pubkey],
@ -623,7 +526,9 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> {
Ok(())
}
fn create_message(
/// Helper to prepare for process_cross_program_instruction()
pub fn create_message(
&mut self,
instruction: &Instruction,
signers: &[Pubkey],
@ -734,7 +639,9 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> {
Ok((message, caller_write_privileges, program_indices))
}
fn process_cross_program_instruction(
/// Process a cross-program instruction
pub fn process_cross_program_instruction(
&mut self,
message: &Message,
program_indices: &[usize],
@ -766,7 +673,9 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> {
self.pop();
result
}
fn process_instruction(&mut self, instruction_data: &[u8]) -> Result<(), InstructionError> {
/// Calls the instruction's program entrypoint method
pub fn process_instruction(&mut self, instruction_data: &[u8]) -> Result<(), InstructionError> {
let keyed_accounts = self.get_keyed_accounts()?;
let root_account = keyed_account_at_index(keyed_accounts, 0)
.map_err(|_| InstructionError::UnsupportedProgramId)?;
@ -802,18 +711,28 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> {
}
Err(InstructionError::UnsupportedProgramId)
}
fn get_caller(&self) -> Result<&Pubkey, InstructionError> {
/// 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)
}
fn get_loader(&self) -> Result<Pubkey, InstructionError> {
/// Get the owner of the currently executing program
pub fn get_loader(&self) -> Result<Pubkey, InstructionError> {
self.get_instruction_keyed_accounts()
.and_then(|keyed_accounts| keyed_accounts.first().ok_or(InstructionError::CallDepth))
.and_then(|keyed_account| keyed_account.owner())
}
fn remove_first_keyed_account(&mut self) -> Result<(), InstructionError> {
/// Removes the first keyed account
#[deprecated(
since = "1.9.0",
note = "To be removed together with remove_native_loader"
)]
pub fn remove_first_keyed_account(&mut self) -> Result<(), InstructionError> {
if !self.is_feature_active(&remove_native_loader::id()) {
let stack_frame = &mut self
.invoke_stack
@ -824,13 +743,17 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> {
}
Ok(())
}
fn get_keyed_accounts(&self) -> Result<&[KeyedAccount], InstructionError> {
/// Get the list of keyed accounts
pub fn get_keyed_accounts(&self) -> Result<&[KeyedAccount], InstructionError> {
self.invoke_stack
.last()
.map(|frame| &frame.keyed_accounts[frame.keyed_accounts_range.clone()])
.ok_or(InstructionError::CallDepth)
}
fn get_instruction_keyed_accounts(&self) -> Result<&[KeyedAccount], InstructionError> {
/// Get the list of keyed accounts skipping `first_instruction_account` many entries
pub fn get_instruction_keyed_accounts(&self) -> Result<&[KeyedAccount], InstructionError> {
let frame = self
.invoke_stack
.last()
@ -841,30 +764,47 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> {
.ok_or(InstructionError::CallDepth)?;
Ok(&frame.keyed_accounts[first_instruction_account..])
}
fn get_log_collector(&self) -> Option<Rc<RefCell<LogCollector>>> {
/// Get this invocation's LogCollector
pub fn get_log_collector(&self) -> Option<Rc<RefCell<LogCollector>>> {
self.log_collector.clone()
}
fn get_compute_meter(&self) -> Rc<RefCell<ComputeMeter>> {
/// Get this invocation's ComputeMeter
pub fn get_compute_meter(&self) -> Rc<RefCell<ComputeMeter>> {
self.compute_meter.clone()
}
fn add_executor(&self, pubkey: &Pubkey, executor: Arc<dyn Executor>) {
/// Loaders may need to do work in order to execute a program. Cache
/// the work that can be re-used across executions
pub fn add_executor(&self, pubkey: &Pubkey, executor: Arc<dyn Executor>) {
self.executors.borrow_mut().insert(*pubkey, executor);
}
fn get_executor(&self, pubkey: &Pubkey) -> Option<Arc<dyn Executor>> {
/// Get the completed loader work that can be re-used across execution
pub fn get_executor(&self, pubkey: &Pubkey) -> Option<Arc<dyn Executor>> {
self.executors.borrow().get(pubkey)
}
fn set_instruction_index(&mut self, instruction_index: usize) {
/// Set which instruction in the message is currently being recorded
pub fn set_instruction_index(&mut self, instruction_index: usize) {
self.instruction_index = instruction_index;
}
fn record_instruction(&self, instruction: &Instruction) {
/// Record invoked instruction
pub fn record_instruction(&self, instruction: &Instruction) {
if let Some(instruction_recorders) = &self.instruction_recorders {
instruction_recorders[self.instruction_index].record_instruction(instruction.clone());
}
}
fn is_feature_active(&self, feature_id: &Pubkey) -> bool {
/// Get the bank's active feature set
pub fn is_feature_active(&self, feature_id: &Pubkey) -> bool {
self.feature_set.is_active(feature_id)
}
fn get_account(&self, pubkey: &Pubkey) -> Option<(usize, Rc<RefCell<AccountSharedData>>)> {
/// Find an account_index and account by its key
pub fn get_account(&self, pubkey: &Pubkey) -> Option<(usize, Rc<RefCell<AccountSharedData>>)> {
for (index, (key, account)) in self.accounts.iter().enumerate().rev() {
if key == pubkey {
return Some((index, account.clone()));
@ -872,7 +812,9 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> {
}
None
}
fn update_timing(
/// Update timing
pub fn update_timing(
&mut self,
serialize_us: u64,
create_vm_us: u64,
@ -884,29 +826,45 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> {
self.timings.execute_us = self.timings.execute_us.saturating_add(execute_us);
self.timings.deserialize_us = self.timings.deserialize_us.saturating_add(deserialize_us);
}
fn get_sysvars(&self) -> &[(Pubkey, Vec<u8>)] {
/// Get cached sysvars
pub fn get_sysvars(&self) -> &[(Pubkey, Vec<u8>)] {
self.sysvars
}
fn get_compute_budget(&self) -> &ComputeBudget {
/// Get this invocation's compute budget
pub fn get_compute_budget(&self) -> &ComputeBudget {
&self.current_compute_budget
}
fn set_blockhash(&mut self, hash: Hash) {
/// Set this invocation's blockhash
pub fn set_blockhash(&mut self, hash: Hash) {
self.blockhash = hash;
}
fn get_blockhash(&self) -> &Hash {
/// Get this invocation's blockhash
pub fn get_blockhash(&self) -> &Hash {
&self.blockhash
}
fn set_lamports_per_signature(&mut self, lamports_per_signature: u64) {
/// Set this invocation's lamports_per_signature value
pub fn set_lamports_per_signature(&mut self, lamports_per_signature: u64) {
self.lamports_per_signature = lamports_per_signature;
}
fn get_lamports_per_signature(&self) -> u64 {
/// Get this invocation's lamports_per_signature value
pub fn get_lamports_per_signature(&self) -> u64 {
self.lamports_per_signature
}
fn set_return_data(&mut self, data: Vec<u8>) -> Result<(), InstructionError> {
/// Set the return data
pub fn set_return_data(&mut self, data: Vec<u8>) -> Result<(), InstructionError> {
self.return_data = (*self.get_caller()?, data);
Ok(())
}
fn get_return_data(&self) -> (Pubkey, &[u8]) {
/// Get the return data
pub fn get_return_data(&self) -> (Pubkey, &[u8]) {
(self.return_data.0, &self.return_data.1)
}
}
@ -914,7 +872,7 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> {
// This method which has a generic parameter is outside of the InvokeContext,
// because the InvokeContext is a dyn Trait.
pub fn get_sysvar<T: Sysvar>(
invoke_context: &dyn InvokeContext,
invoke_context: &InvokeContext,
id: &Pubkey,
) -> Result<T, InstructionError> {
invoke_context
@ -994,7 +952,7 @@ pub fn prepare_mock_invoke_context(
}
}
pub fn with_mock_invoke_context<R, F: FnMut(&mut ThisInvokeContext) -> R>(
pub fn with_mock_invoke_context<R, F: FnMut(&mut InvokeContext) -> R>(
loader_id: Pubkey,
account_size: usize,
mut callback: F,
@ -1021,7 +979,7 @@ pub fn with_mock_invoke_context<R, F: FnMut(&mut ThisInvokeContext) -> R>(
),
];
let preparation = prepare_mock_invoke_context(&program_indices, &[], &keyed_accounts);
let mut invoke_context = ThisInvokeContext::new_mock(&preparation.accounts, &[]);
let mut invoke_context = InvokeContext::new_mock(&preparation.accounts, &[]);
invoke_context
.push(
&preparation.message,
@ -1045,7 +1003,7 @@ pub fn mock_process_instruction(
let processor_account = AccountSharedData::new_ref(0, 0, &solana_sdk::native_loader::id());
program_indices.insert(0, preparation.accounts.len());
preparation.accounts.push((*loader_id, processor_account));
let mut invoke_context = ThisInvokeContext::new_mock(&preparation.accounts, &[]);
let mut invoke_context = InvokeContext::new_mock(&preparation.accounts, &[]);
invoke_context.push(
&preparation.message,
&preparation.message.instructions[0],
@ -1081,7 +1039,7 @@ mod tests {
fn mock_process_instruction(
_first_instruction_account: usize,
_data: &[u8],
_invoke_context: &mut dyn InvokeContext,
_invoke_context: &mut InvokeContext,
) -> Result<(), InstructionError> {
Ok(())
}
@ -1089,7 +1047,7 @@ mod tests {
fn mock_ix_processor(
_first_instruction_account: usize,
_data: &[u8],
_context: &mut dyn InvokeContext,
_context: &mut InvokeContext,
) -> Result<(), InstructionError> {
Ok(())
}
@ -1110,7 +1068,7 @@ mod tests {
fn mock_process_instruction(
first_instruction_account: usize,
data: &[u8],
invoke_context: &mut dyn InvokeContext,
invoke_context: &mut InvokeContext,
) -> Result<(), InstructionError> {
let program_id = invoke_context.get_caller()?;
let keyed_accounts = invoke_context.get_keyed_accounts()?;
@ -1184,7 +1142,7 @@ mod tests {
&[Instruction::new_with_bytes(invoke_stack[0], &[0], metas)],
None,
);
let mut invoke_context = ThisInvokeContext::new_mock(&accounts, &[]);
let mut invoke_context = InvokeContext::new_mock(&accounts, &[]);
// Check call depth increases and has a limit
let mut depth_reached = 0;
@ -1271,7 +1229,7 @@ mod tests {
)],
None,
);
let mut invoke_context = ThisInvokeContext::new_mock(&accounts, &[]);
let mut invoke_context = InvokeContext::new_mock(&accounts, &[]);
invoke_context
.push(&message, &message.instructions[0], &[0], None)
.unwrap();
@ -1336,7 +1294,7 @@ mod tests {
program_id: callee_program_id,
process_instruction: mock_process_instruction,
}];
let mut invoke_context = ThisInvokeContext::new_mock(&accounts, builtin_programs);
let mut invoke_context = InvokeContext::new_mock(&accounts, builtin_programs);
invoke_context
.push(&message, &caller_instruction, &program_indices[..1], None)
.unwrap();
@ -1463,7 +1421,7 @@ mod tests {
program_id: callee_program_id,
process_instruction: mock_process_instruction,
}];
let mut invoke_context = ThisInvokeContext::new_mock(&accounts, builtin_programs);
let mut invoke_context = InvokeContext::new_mock(&accounts, builtin_programs);
invoke_context
.push(&message, &caller_instruction, &program_indices, None)
.unwrap();
@ -1551,7 +1509,7 @@ 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 mut invoke_context = ThisInvokeContext::new_mock_with_sysvars_and_features(
let mut invoke_context = InvokeContext::new_mock_with_sysvars_and_features(
&accounts,
&[],
&[],

View File

@ -33,7 +33,7 @@ use thiserror::Error;
pub type LoaderEntrypoint = unsafe extern "C" fn(
program_id: &Pubkey,
instruction_data: &[u8],
invoke_context: &dyn InvokeContext,
invoke_context: &InvokeContext,
) -> Result<(), InstructionError>;
// Prototype of a native program entry point
@ -165,7 +165,7 @@ impl NativeLoader {
&self,
first_instruction_account: usize,
instruction_data: &[u8],
invoke_context: &mut dyn InvokeContext,
invoke_context: &mut InvokeContext,
) -> Result<(), InstructionError> {
let (program_id, name_vec) = {
let program_id = invoke_context.get_caller()?;