This reverts commit df6905c3a6
.
This commit is contained in:
@ -603,7 +603,7 @@ impl InstructionProcessor {
|
|||||||
message,
|
message,
|
||||||
instruction,
|
instruction,
|
||||||
program_indices,
|
program_indices,
|
||||||
Some(account_indices),
|
account_indices,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let mut instruction_processor = InstructionProcessor::default();
|
let mut instruction_processor = InstructionProcessor::default();
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
//! `Bank::process_transactions`
|
//! `Bank::process_transactions`
|
||||||
//!
|
//!
|
||||||
//! It does this by loading the accounts using the reference it holds on the account store,
|
//! It does this by loading the accounts using the reference it holds on the account store,
|
||||||
//! and then passing those to an InvokeContext which handles loading the programs specified
|
//! and then passing those to the message_processor which handles loading the programs specified
|
||||||
//! by the Transaction and executing it.
|
//! by the Transaction and executing it.
|
||||||
//!
|
//!
|
||||||
//! The bank then stores the results to the accounts store.
|
//! The bank then stores the results to the accounts store.
|
||||||
@ -65,7 +65,7 @@ use log::*;
|
|||||||
use rayon::ThreadPool;
|
use rayon::ThreadPool;
|
||||||
use solana_measure::measure::Measure;
|
use solana_measure::measure::Measure;
|
||||||
use solana_metrics::{datapoint_debug, inc_new_counter_debug, inc_new_counter_info};
|
use solana_metrics::{datapoint_debug, inc_new_counter_debug, inc_new_counter_info};
|
||||||
use solana_program_runtime::{ExecuteDetailsTimings, Executors, InstructionProcessor};
|
use solana_program_runtime::{ExecuteDetailsTimings, Executors};
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
use solana_sdk::recent_blockhashes_account;
|
use solana_sdk::recent_blockhashes_account;
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
@ -945,8 +945,8 @@ pub struct Bank {
|
|||||||
/// stream for the slot == self.slot
|
/// stream for the slot == self.slot
|
||||||
is_delta: AtomicBool,
|
is_delta: AtomicBool,
|
||||||
|
|
||||||
/// The InstructionProcessor
|
/// The Message processor
|
||||||
instruction_processor: InstructionProcessor,
|
message_processor: MessageProcessor,
|
||||||
|
|
||||||
compute_budget: Option<ComputeBudget>,
|
compute_budget: Option<ComputeBudget>,
|
||||||
|
|
||||||
@ -1095,7 +1095,7 @@ impl Bank {
|
|||||||
stakes: RwLock::<Stakes>::default(),
|
stakes: RwLock::<Stakes>::default(),
|
||||||
epoch_stakes: HashMap::<Epoch, EpochStakes>::default(),
|
epoch_stakes: HashMap::<Epoch, EpochStakes>::default(),
|
||||||
is_delta: AtomicBool::default(),
|
is_delta: AtomicBool::default(),
|
||||||
instruction_processor: InstructionProcessor::default(),
|
message_processor: MessageProcessor::default(),
|
||||||
compute_budget: Option::<ComputeBudget>::default(),
|
compute_budget: Option::<ComputeBudget>::default(),
|
||||||
feature_builtins: Arc::<Vec<(Builtin, Pubkey, ActivationType)>>::default(),
|
feature_builtins: Arc::<Vec<(Builtin, Pubkey, ActivationType)>>::default(),
|
||||||
last_vote_sync: AtomicU64::default(),
|
last_vote_sync: AtomicU64::default(),
|
||||||
@ -1327,7 +1327,7 @@ impl Bank {
|
|||||||
is_delta: AtomicBool::new(false),
|
is_delta: AtomicBool::new(false),
|
||||||
tick_height: AtomicU64::new(parent.tick_height.load(Relaxed)),
|
tick_height: AtomicU64::new(parent.tick_height.load(Relaxed)),
|
||||||
signature_count: AtomicU64::new(0),
|
signature_count: AtomicU64::new(0),
|
||||||
instruction_processor: parent.instruction_processor.clone(),
|
message_processor: parent.message_processor.clone(),
|
||||||
compute_budget: parent.compute_budget,
|
compute_budget: parent.compute_budget,
|
||||||
feature_builtins: parent.feature_builtins.clone(),
|
feature_builtins: parent.feature_builtins.clone(),
|
||||||
hard_forks: parent.hard_forks.clone(),
|
hard_forks: parent.hard_forks.clone(),
|
||||||
@ -1482,7 +1482,7 @@ impl Bank {
|
|||||||
stakes: RwLock::new(fields.stakes),
|
stakes: RwLock::new(fields.stakes),
|
||||||
epoch_stakes: fields.epoch_stakes,
|
epoch_stakes: fields.epoch_stakes,
|
||||||
is_delta: AtomicBool::new(fields.is_delta),
|
is_delta: AtomicBool::new(fields.is_delta),
|
||||||
instruction_processor: new(),
|
message_processor: new(),
|
||||||
compute_budget: None,
|
compute_budget: None,
|
||||||
feature_builtins: new(),
|
feature_builtins: new(),
|
||||||
last_vote_sync: new(),
|
last_vote_sync: new(),
|
||||||
@ -3389,8 +3389,7 @@ impl Bank {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let Some(legacy_message) = tx.message().legacy_message() {
|
if let Some(legacy_message) = tx.message().legacy_message() {
|
||||||
process_result = MessageProcessor::process_message(
|
process_result = self.message_processor.process_message(
|
||||||
&self.instruction_processor,
|
|
||||||
legacy_message,
|
legacy_message,
|
||||||
&loaded_transaction.program_indices,
|
&loaded_transaction.program_indices,
|
||||||
&account_refcells,
|
&account_refcells,
|
||||||
@ -5326,7 +5325,7 @@ impl Bank {
|
|||||||
) {
|
) {
|
||||||
debug!("Adding program {} under {:?}", name, program_id);
|
debug!("Adding program {} under {:?}", name, program_id);
|
||||||
self.add_native_program(name, &program_id, false);
|
self.add_native_program(name, &program_id, false);
|
||||||
self.instruction_processor
|
self.message_processor
|
||||||
.add_program(program_id, process_instruction_with_context);
|
.add_program(program_id, process_instruction_with_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5339,7 +5338,7 @@ impl Bank {
|
|||||||
) {
|
) {
|
||||||
debug!("Replacing program {} under {:?}", name, program_id);
|
debug!("Replacing program {} under {:?}", name, program_id);
|
||||||
self.add_native_program(name, &program_id, true);
|
self.add_native_program(name, &program_id, true);
|
||||||
self.instruction_processor
|
self.message_processor
|
||||||
.add_program(program_id, process_instruction_with_context);
|
.add_program(program_id, process_instruction_with_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,7 +47,6 @@ impl ComputeMeter for ThisComputeMeter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub struct ThisInvokeContext<'a> {
|
pub struct ThisInvokeContext<'a> {
|
||||||
instruction_index: usize,
|
|
||||||
invoke_stack: Vec<InvokeContextStackFrame<'a>>,
|
invoke_stack: Vec<InvokeContextStackFrame<'a>>,
|
||||||
rent: Rent,
|
rent: Rent,
|
||||||
pre_accounts: Vec<PreAccount>,
|
pre_accounts: Vec<PreAccount>,
|
||||||
@ -59,7 +58,7 @@ pub struct ThisInvokeContext<'a> {
|
|||||||
bpf_compute_budget: solana_sdk::process_instruction::BpfComputeBudget,
|
bpf_compute_budget: solana_sdk::process_instruction::BpfComputeBudget,
|
||||||
compute_meter: Rc<RefCell<dyn ComputeMeter>>,
|
compute_meter: Rc<RefCell<dyn ComputeMeter>>,
|
||||||
executors: Rc<RefCell<Executors>>,
|
executors: Rc<RefCell<Executors>>,
|
||||||
instruction_recorders: Option<&'a [InstructionRecorder]>,
|
instruction_recorder: Option<InstructionRecorder>,
|
||||||
feature_set: Arc<FeatureSet>,
|
feature_set: Arc<FeatureSet>,
|
||||||
pub timings: ExecuteDetailsTimings,
|
pub timings: ExecuteDetailsTimings,
|
||||||
account_db: Arc<Accounts>,
|
account_db: Arc<Accounts>,
|
||||||
@ -74,20 +73,25 @@ pub struct ThisInvokeContext<'a> {
|
|||||||
impl<'a> ThisInvokeContext<'a> {
|
impl<'a> ThisInvokeContext<'a> {
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
|
program_id: &Pubkey,
|
||||||
rent: Rent,
|
rent: Rent,
|
||||||
|
message: &'a Message,
|
||||||
|
instruction: &'a CompiledInstruction,
|
||||||
|
program_indices: &[usize],
|
||||||
accounts: &'a [(Pubkey, Rc<RefCell<AccountSharedData>>)],
|
accounts: &'a [(Pubkey, Rc<RefCell<AccountSharedData>>)],
|
||||||
programs: &'a [(Pubkey, ProcessInstructionWithContext)],
|
programs: &'a [(Pubkey, ProcessInstructionWithContext)],
|
||||||
log_collector: Option<Rc<LogCollector>>,
|
log_collector: Option<Rc<LogCollector>>,
|
||||||
compute_budget: ComputeBudget,
|
compute_budget: ComputeBudget,
|
||||||
compute_meter: Rc<RefCell<dyn ComputeMeter>>,
|
compute_meter: Rc<RefCell<dyn ComputeMeter>>,
|
||||||
executors: Rc<RefCell<Executors>>,
|
executors: Rc<RefCell<Executors>>,
|
||||||
instruction_recorders: Option<&'a [InstructionRecorder]>,
|
instruction_recorder: Option<InstructionRecorder>,
|
||||||
feature_set: Arc<FeatureSet>,
|
feature_set: Arc<FeatureSet>,
|
||||||
account_db: Arc<Accounts>,
|
account_db: Arc<Accounts>,
|
||||||
ancestors: &'a Ancestors,
|
ancestors: &'a Ancestors,
|
||||||
blockhash: &'a Hash,
|
blockhash: &'a Hash,
|
||||||
fee_calculator: &'a FeeCalculator,
|
fee_calculator: &'a FeeCalculator,
|
||||||
) -> Self {
|
) -> Result<Self, InstructionError> {
|
||||||
|
let pre_accounts = MessageProcessor::create_pre_accounts(message, instruction, accounts);
|
||||||
let compute_meter = if feature_set.is_active(&tx_wide_compute_cap::id()) {
|
let compute_meter = if feature_set.is_active(&tx_wide_compute_cap::id()) {
|
||||||
compute_meter
|
compute_meter
|
||||||
} else {
|
} else {
|
||||||
@ -95,11 +99,10 @@ impl<'a> ThisInvokeContext<'a> {
|
|||||||
remaining: compute_budget.max_units,
|
remaining: compute_budget.max_units,
|
||||||
}))
|
}))
|
||||||
};
|
};
|
||||||
Self {
|
let mut invoke_context = Self {
|
||||||
instruction_index: 0,
|
|
||||||
invoke_stack: Vec::with_capacity(compute_budget.max_invoke_depth),
|
invoke_stack: Vec::with_capacity(compute_budget.max_invoke_depth),
|
||||||
rent,
|
rent,
|
||||||
pre_accounts: Vec::new(),
|
pre_accounts,
|
||||||
accounts,
|
accounts,
|
||||||
programs,
|
programs,
|
||||||
logger: Rc::new(RefCell::new(ThisLogger { log_collector })),
|
logger: Rc::new(RefCell::new(ThisLogger { log_collector })),
|
||||||
@ -107,7 +110,7 @@ impl<'a> ThisInvokeContext<'a> {
|
|||||||
bpf_compute_budget: compute_budget.into(),
|
bpf_compute_budget: compute_budget.into(),
|
||||||
compute_meter,
|
compute_meter,
|
||||||
executors,
|
executors,
|
||||||
instruction_recorders,
|
instruction_recorder,
|
||||||
feature_set,
|
feature_set,
|
||||||
timings: ExecuteDetailsTimings::default(),
|
timings: ExecuteDetailsTimings::default(),
|
||||||
account_db,
|
account_db,
|
||||||
@ -116,7 +119,16 @@ impl<'a> ThisInvokeContext<'a> {
|
|||||||
blockhash,
|
blockhash,
|
||||||
fee_calculator,
|
fee_calculator,
|
||||||
return_data: None,
|
return_data: None,
|
||||||
}
|
};
|
||||||
|
let account_indices = (0..accounts.len()).collect::<Vec<usize>>();
|
||||||
|
invoke_context.push(
|
||||||
|
program_id,
|
||||||
|
message,
|
||||||
|
instruction,
|
||||||
|
program_indices,
|
||||||
|
&account_indices,
|
||||||
|
)?;
|
||||||
|
Ok(invoke_context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<'a> InvokeContext for ThisInvokeContext<'a> {
|
impl<'a> InvokeContext for ThisInvokeContext<'a> {
|
||||||
@ -126,27 +138,12 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> {
|
|||||||
message: &Message,
|
message: &Message,
|
||||||
instruction: &CompiledInstruction,
|
instruction: &CompiledInstruction,
|
||||||
program_indices: &[usize],
|
program_indices: &[usize],
|
||||||
account_indices: Option<&[usize]>,
|
account_indices: &[usize],
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
if self.invoke_stack.len() > self.compute_budget.max_invoke_depth {
|
if self.invoke_stack.len() > self.compute_budget.max_invoke_depth {
|
||||||
return Err(InstructionError::CallDepth);
|
return Err(InstructionError::CallDepth);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.invoke_stack.is_empty() {
|
|
||||||
self.pre_accounts = Vec::with_capacity(instruction.accounts.len());
|
|
||||||
let mut work = |_unique_index: usize, account_index: usize| {
|
|
||||||
if account_index < message.account_keys.len() && account_index < self.accounts.len()
|
|
||||||
{
|
|
||||||
let account = self.accounts[account_index].1.borrow();
|
|
||||||
self.pre_accounts
|
|
||||||
.push(PreAccount::new(&self.accounts[account_index].0, &account));
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
Err(InstructionError::MissingAccount)
|
|
||||||
};
|
|
||||||
instruction.visit_each_account(&mut work)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let contains = self.invoke_stack.iter().any(|frame| frame.key == *key);
|
let contains = self.invoke_stack.iter().any(|frame| frame.key == *key);
|
||||||
let is_last = if let Some(last_frame) = self.invoke_stack.last() {
|
let is_last = if let Some(last_frame) = self.invoke_stack.last() {
|
||||||
last_frame.key == *key
|
last_frame.key == *key
|
||||||
@ -174,11 +171,7 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> {
|
|||||||
})
|
})
|
||||||
.chain(instruction.accounts.iter().map(|index_in_instruction| {
|
.chain(instruction.accounts.iter().map(|index_in_instruction| {
|
||||||
let index_in_instruction = *index_in_instruction as usize;
|
let index_in_instruction = *index_in_instruction as usize;
|
||||||
let account_index = if let Some(account_indices) = account_indices {
|
let account_index = account_indices[index_in_instruction];
|
||||||
account_indices[index_in_instruction]
|
|
||||||
} else {
|
|
||||||
index_in_instruction
|
|
||||||
};
|
|
||||||
(
|
(
|
||||||
message.is_signer(index_in_instruction),
|
message.is_signer(index_in_instruction),
|
||||||
message.is_writable(index_in_instruction, demote_program_write_locks),
|
message.is_writable(index_in_instruction, demote_program_write_locks),
|
||||||
@ -199,66 +192,6 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> {
|
|||||||
fn invoke_depth(&self) -> usize {
|
fn invoke_depth(&self) -> usize {
|
||||||
self.invoke_stack.len()
|
self.invoke_stack.len()
|
||||||
}
|
}
|
||||||
fn verify(
|
|
||||||
&mut self,
|
|
||||||
message: &Message,
|
|
||||||
instruction: &CompiledInstruction,
|
|
||||||
program_indices: &[usize],
|
|
||||||
) -> Result<(), InstructionError> {
|
|
||||||
let program_id = instruction.program_id(&message.account_keys);
|
|
||||||
let demote_program_write_locks = self.is_feature_active(&demote_program_write_locks::id());
|
|
||||||
let do_support_realloc = self.feature_set.is_active(&do_support_realloc::id());
|
|
||||||
|
|
||||||
// Verify all executable accounts have zero outstanding refs
|
|
||||||
for account_index in program_indices.iter() {
|
|
||||||
self.accounts[*account_index]
|
|
||||||
.1
|
|
||||||
.try_borrow_mut()
|
|
||||||
.map_err(|_| InstructionError::AccountBorrowOutstanding)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify the per-account instruction results
|
|
||||||
let (mut pre_sum, mut post_sum) = (0_u128, 0_u128);
|
|
||||||
let mut work = |unique_index: usize, account_index: usize| {
|
|
||||||
{
|
|
||||||
// Verify account has no outstanding references
|
|
||||||
let _ = self.accounts[account_index]
|
|
||||||
.1
|
|
||||||
.try_borrow_mut()
|
|
||||||
.map_err(|_| InstructionError::AccountBorrowOutstanding)?;
|
|
||||||
}
|
|
||||||
let account = self.accounts[account_index].1.borrow();
|
|
||||||
self.pre_accounts[unique_index]
|
|
||||||
.verify(
|
|
||||||
program_id,
|
|
||||||
message.is_writable(account_index, demote_program_write_locks),
|
|
||||||
&self.rent,
|
|
||||||
&account,
|
|
||||||
&mut self.timings,
|
|
||||||
true,
|
|
||||||
do_support_realloc,
|
|
||||||
)
|
|
||||||
.map_err(|err| {
|
|
||||||
ic_logger_msg!(
|
|
||||||
self.logger,
|
|
||||||
"failed to verify account {}: {}",
|
|
||||||
self.pre_accounts[unique_index].key(),
|
|
||||||
err
|
|
||||||
);
|
|
||||||
err
|
|
||||||
})?;
|
|
||||||
pre_sum += u128::from(self.pre_accounts[unique_index].lamports());
|
|
||||||
post_sum += u128::from(account.lamports());
|
|
||||||
Ok(())
|
|
||||||
};
|
|
||||||
instruction.visit_each_account(&mut work)?;
|
|
||||||
|
|
||||||
// Verify that the total sum of all the lamports did not change
|
|
||||||
if pre_sum != post_sum {
|
|
||||||
return Err(InstructionError::UnbalancedInstruction);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn verify_and_update(
|
fn verify_and_update(
|
||||||
&mut self,
|
&mut self,
|
||||||
instruction: &CompiledInstruction,
|
instruction: &CompiledInstruction,
|
||||||
@ -272,7 +205,7 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> {
|
|||||||
.ok_or(InstructionError::CallDepth)?;
|
.ok_or(InstructionError::CallDepth)?;
|
||||||
let program_id = &stack_frame.key;
|
let program_id = &stack_frame.key;
|
||||||
let rent = &self.rent;
|
let rent = &self.rent;
|
||||||
let logger = &self.logger;
|
let logger = self.get_logger();
|
||||||
let accounts = &self.accounts;
|
let accounts = &self.accounts;
|
||||||
let pre_accounts = &mut self.pre_accounts;
|
let pre_accounts = &mut self.pre_accounts;
|
||||||
let timings = &mut self.timings;
|
let timings = &mut self.timings;
|
||||||
@ -369,12 +302,9 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> {
|
|||||||
fn get_executor(&self, pubkey: &Pubkey) -> Option<Arc<dyn Executor>> {
|
fn get_executor(&self, pubkey: &Pubkey) -> Option<Arc<dyn Executor>> {
|
||||||
self.executors.borrow().get(pubkey)
|
self.executors.borrow().get(pubkey)
|
||||||
}
|
}
|
||||||
fn set_instruction_index(&mut self, instruction_index: usize) {
|
|
||||||
self.instruction_index = instruction_index;
|
|
||||||
}
|
|
||||||
fn record_instruction(&self, instruction: &Instruction) {
|
fn record_instruction(&self, instruction: &Instruction) {
|
||||||
if let Some(instruction_recorders) = &self.instruction_recorders {
|
if let Some(recorder) = &self.instruction_recorder {
|
||||||
instruction_recorders[self.instruction_index].record_instruction(instruction.clone());
|
recorder.record_instruction(instruction.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn is_feature_active(&self, feature_id: &Pubkey) -> bool {
|
fn is_feature_active(&self, feature_id: &Pubkey) -> bool {
|
||||||
@ -452,7 +382,10 @@ impl Logger for ThisLogger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Default, Clone, Deserialize, Serialize)]
|
||||||
pub struct MessageProcessor {}
|
pub struct MessageProcessor {
|
||||||
|
#[serde(skip)]
|
||||||
|
instruction_processor: InstructionProcessor,
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(RUSTC_WITH_SPECIALIZATION)]
|
#[cfg(RUSTC_WITH_SPECIALIZATION)]
|
||||||
impl ::solana_frozen_abi::abi_example::AbiExample for MessageProcessor {
|
impl ::solana_frozen_abi::abi_example::AbiExample for MessageProcessor {
|
||||||
@ -464,15 +397,215 @@ impl ::solana_frozen_abi::abi_example::AbiExample for MessageProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl MessageProcessor {
|
impl MessageProcessor {
|
||||||
/// Process a message.
|
/// Add a static entrypoint to intercept instructions before the dynamic loader.
|
||||||
/// This method calls each instruction in the message over the set of loaded accounts.
|
pub fn add_program(
|
||||||
/// For each instruction it calls the program entrypoint method and verifies that the result of
|
&mut self,
|
||||||
|
program_id: Pubkey,
|
||||||
|
process_instruction: ProcessInstructionWithContext,
|
||||||
|
) {
|
||||||
|
self.instruction_processor
|
||||||
|
.add_program(program_id, process_instruction);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Record the initial state of the accounts so that they can be compared
|
||||||
|
/// after the instruction is processed
|
||||||
|
pub fn create_pre_accounts(
|
||||||
|
message: &Message,
|
||||||
|
instruction: &CompiledInstruction,
|
||||||
|
accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)],
|
||||||
|
) -> Vec<PreAccount> {
|
||||||
|
let mut pre_accounts = Vec::with_capacity(instruction.accounts.len());
|
||||||
|
{
|
||||||
|
let mut work = |_unique_index: usize, account_index: usize| {
|
||||||
|
if account_index < message.account_keys.len() && account_index < accounts.len() {
|
||||||
|
let account = accounts[account_index].1.borrow();
|
||||||
|
pre_accounts.push(PreAccount::new(&accounts[account_index].0, &account));
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
Err(InstructionError::MissingAccount)
|
||||||
|
};
|
||||||
|
let _ = instruction.visit_each_account(&mut work);
|
||||||
|
}
|
||||||
|
pre_accounts
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Verify there are no outstanding borrows
|
||||||
|
pub fn verify_account_references(
|
||||||
|
accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)],
|
||||||
|
program_indices: &[usize],
|
||||||
|
) -> Result<(), InstructionError> {
|
||||||
|
for account_index in program_indices.iter() {
|
||||||
|
accounts[*account_index]
|
||||||
|
.1
|
||||||
|
.try_borrow_mut()
|
||||||
|
.map_err(|_| InstructionError::AccountBorrowOutstanding)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Verify the results of an instruction
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
pub fn verify(
|
||||||
|
message: &Message,
|
||||||
|
instruction: &CompiledInstruction,
|
||||||
|
pre_accounts: &[PreAccount],
|
||||||
|
program_indices: &[usize],
|
||||||
|
accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)],
|
||||||
|
rent: &Rent,
|
||||||
|
timings: &mut ExecuteDetailsTimings,
|
||||||
|
logger: Rc<RefCell<dyn Logger>>,
|
||||||
|
demote_program_write_locks: bool,
|
||||||
|
do_support_realloc: bool,
|
||||||
|
) -> Result<(), InstructionError> {
|
||||||
|
// Verify all executable accounts have zero outstanding refs
|
||||||
|
Self::verify_account_references(accounts, program_indices)?;
|
||||||
|
|
||||||
|
// Verify the per-account instruction results
|
||||||
|
let (mut pre_sum, mut post_sum) = (0_u128, 0_u128);
|
||||||
|
{
|
||||||
|
let program_id = instruction.program_id(&message.account_keys);
|
||||||
|
let mut work = |unique_index: usize, account_index: usize| {
|
||||||
|
{
|
||||||
|
// Verify account has no outstanding references
|
||||||
|
let _ = accounts[account_index]
|
||||||
|
.1
|
||||||
|
.try_borrow_mut()
|
||||||
|
.map_err(|_| InstructionError::AccountBorrowOutstanding)?;
|
||||||
|
}
|
||||||
|
let account = accounts[account_index].1.borrow();
|
||||||
|
pre_accounts[unique_index]
|
||||||
|
.verify(
|
||||||
|
program_id,
|
||||||
|
message.is_writable(account_index, demote_program_write_locks),
|
||||||
|
rent,
|
||||||
|
&account,
|
||||||
|
timings,
|
||||||
|
true,
|
||||||
|
do_support_realloc,
|
||||||
|
)
|
||||||
|
.map_err(|err| {
|
||||||
|
ic_logger_msg!(
|
||||||
|
logger,
|
||||||
|
"failed to verify account {}: {}",
|
||||||
|
pre_accounts[unique_index].key(),
|
||||||
|
err
|
||||||
|
);
|
||||||
|
err
|
||||||
|
})?;
|
||||||
|
pre_sum += u128::from(pre_accounts[unique_index].lamports());
|
||||||
|
post_sum += u128::from(account.lamports());
|
||||||
|
Ok(())
|
||||||
|
};
|
||||||
|
instruction.visit_each_account(&mut work)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that the total sum of all the lamports did not change
|
||||||
|
if pre_sum != post_sum {
|
||||||
|
return Err(InstructionError::UnbalancedInstruction);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Execute an instruction
|
||||||
|
/// This method calls the instruction's program entrypoint method and verifies that the result of
|
||||||
/// the call does not violate the bank's accounting rules.
|
/// the call does not violate the bank's accounting rules.
|
||||||
/// The accounts are committed back to the bank only if every instruction succeeds.
|
/// The accounts are committed back to the bank only if this function returns Ok(_).
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
fn execute_instruction(
|
||||||
|
&self,
|
||||||
|
message: &Message,
|
||||||
|
instruction: &CompiledInstruction,
|
||||||
|
program_indices: &[usize],
|
||||||
|
accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)],
|
||||||
|
rent_collector: &RentCollector,
|
||||||
|
log_collector: Option<Rc<LogCollector>>,
|
||||||
|
executors: Rc<RefCell<Executors>>,
|
||||||
|
instruction_recorder: Option<InstructionRecorder>,
|
||||||
|
instruction_index: usize,
|
||||||
|
feature_set: Arc<FeatureSet>,
|
||||||
|
compute_budget: ComputeBudget,
|
||||||
|
compute_meter: Rc<RefCell<dyn ComputeMeter>>,
|
||||||
|
timings: &mut ExecuteDetailsTimings,
|
||||||
|
account_db: Arc<Accounts>,
|
||||||
|
ancestors: &Ancestors,
|
||||||
|
blockhash: &Hash,
|
||||||
|
fee_calculator: &FeeCalculator,
|
||||||
|
) -> Result<(), InstructionError> {
|
||||||
|
// Fixup the special instructions key if present
|
||||||
|
// before the account pre-values are taken care of
|
||||||
|
for (pubkey, accont) in accounts.iter().take(message.account_keys.len()) {
|
||||||
|
if instructions::check_id(pubkey) {
|
||||||
|
let mut mut_account_ref = accont.borrow_mut();
|
||||||
|
instructions::store_current_index(
|
||||||
|
mut_account_ref.data_as_mut_slice(),
|
||||||
|
instruction_index as u16,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let program_id = instruction.program_id(&message.account_keys);
|
||||||
|
|
||||||
|
let mut compute_budget = compute_budget;
|
||||||
|
if feature_set.is_active(&neon_evm_compute_budget::id())
|
||||||
|
&& *program_id == crate::neon_evm_program::id()
|
||||||
|
{
|
||||||
|
// Bump the compute budget for neon_evm
|
||||||
|
compute_budget.max_units = compute_budget.max_units.max(500_000);
|
||||||
|
compute_budget.heap_size = Some(256 * 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
let programs = self.instruction_processor.programs();
|
||||||
|
let mut invoke_context = ThisInvokeContext::new(
|
||||||
|
program_id,
|
||||||
|
rent_collector.rent,
|
||||||
|
message,
|
||||||
|
instruction,
|
||||||
|
program_indices,
|
||||||
|
accounts,
|
||||||
|
programs,
|
||||||
|
log_collector,
|
||||||
|
compute_budget,
|
||||||
|
compute_meter,
|
||||||
|
executors,
|
||||||
|
instruction_recorder,
|
||||||
|
feature_set,
|
||||||
|
account_db,
|
||||||
|
ancestors,
|
||||||
|
blockhash,
|
||||||
|
fee_calculator,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
self.instruction_processor.process_instruction(
|
||||||
|
program_id,
|
||||||
|
&instruction.data,
|
||||||
|
&mut invoke_context,
|
||||||
|
)?;
|
||||||
|
Self::verify(
|
||||||
|
message,
|
||||||
|
instruction,
|
||||||
|
&invoke_context.pre_accounts,
|
||||||
|
program_indices,
|
||||||
|
accounts,
|
||||||
|
&rent_collector.rent,
|
||||||
|
timings,
|
||||||
|
invoke_context.get_logger(),
|
||||||
|
invoke_context.is_feature_active(&demote_program_write_locks::id()),
|
||||||
|
invoke_context.is_feature_active(&do_support_realloc::id()),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
timings.accumulate(&invoke_context.timings);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Process a message.
|
||||||
|
/// This method calls each instruction in the message over the set of loaded Accounts
|
||||||
|
/// The accounts are committed back to the bank only if every instruction succeeds
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
pub fn process_message(
|
pub fn process_message(
|
||||||
instruction_processor: &InstructionProcessor,
|
&self,
|
||||||
message: &Message,
|
message: &Message,
|
||||||
program_indices: &[Vec<usize>],
|
program_indices: &[Vec<usize>],
|
||||||
accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)],
|
accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)],
|
||||||
@ -489,80 +622,43 @@ impl MessageProcessor {
|
|||||||
blockhash: Hash,
|
blockhash: Hash,
|
||||||
fee_calculator: FeeCalculator,
|
fee_calculator: FeeCalculator,
|
||||||
) -> Result<(), TransactionError> {
|
) -> Result<(), TransactionError> {
|
||||||
let mut invoke_context = ThisInvokeContext::new(
|
for (instruction_index, instruction) in message.instructions.iter().enumerate() {
|
||||||
rent_collector.rent,
|
|
||||||
accounts,
|
|
||||||
instruction_processor.programs(),
|
|
||||||
log_collector,
|
|
||||||
compute_budget,
|
|
||||||
compute_meter,
|
|
||||||
executors,
|
|
||||||
instruction_recorders,
|
|
||||||
feature_set,
|
|
||||||
account_db,
|
|
||||||
ancestors,
|
|
||||||
&blockhash,
|
|
||||||
&fee_calculator,
|
|
||||||
);
|
|
||||||
let compute_meter = invoke_context.get_compute_meter();
|
|
||||||
for (instruction_index, (instruction, program_indices)) in message
|
|
||||||
.instructions
|
|
||||||
.iter()
|
|
||||||
.zip(program_indices.iter())
|
|
||||||
.enumerate()
|
|
||||||
{
|
|
||||||
let mut time = Measure::start("execute_instruction");
|
let mut time = Measure::start("execute_instruction");
|
||||||
let pre_remaining_units = compute_meter.borrow().get_remaining();
|
let pre_remaining_units = compute_meter.borrow().get_remaining();
|
||||||
|
let instruction_recorder = instruction_recorders
|
||||||
// Fixup the special instructions key if present
|
.as_ref()
|
||||||
// before the account pre-values are taken care of
|
.map(|recorders| recorders[instruction_index].clone());
|
||||||
for (pubkey, account) in accounts.iter().take(message.account_keys.len()) {
|
let err = self
|
||||||
if instructions::check_id(pubkey) {
|
.execute_instruction(
|
||||||
let mut mut_account_ref = account.borrow_mut();
|
message,
|
||||||
instructions::store_current_index(
|
instruction,
|
||||||
mut_account_ref.data_as_mut_slice(),
|
&program_indices[instruction_index],
|
||||||
instruction_index as u16,
|
accounts,
|
||||||
);
|
rent_collector,
|
||||||
break;
|
log_collector.clone(),
|
||||||
}
|
executors.clone(),
|
||||||
}
|
instruction_recorder,
|
||||||
|
instruction_index,
|
||||||
let program_id = instruction.program_id(&message.account_keys);
|
feature_set.clone(),
|
||||||
|
compute_budget,
|
||||||
let mut compute_budget = compute_budget;
|
compute_meter.clone(),
|
||||||
if invoke_context.is_feature_active(&neon_evm_compute_budget::id())
|
timings,
|
||||||
&& *program_id == crate::neon_evm_program::id()
|
account_db.clone(),
|
||||||
{
|
ancestors,
|
||||||
// Bump the compute budget for neon_evm
|
&blockhash,
|
||||||
compute_budget.max_units = compute_budget.max_units.max(500_000);
|
&fee_calculator,
|
||||||
compute_budget.heap_size = Some(256 * 1024);
|
)
|
||||||
}
|
|
||||||
|
|
||||||
invoke_context.set_instruction_index(instruction_index);
|
|
||||||
let result = invoke_context
|
|
||||||
.push(program_id, message, instruction, program_indices, None)
|
|
||||||
.and_then(|()| {
|
|
||||||
instruction_processor.process_instruction(
|
|
||||||
program_id,
|
|
||||||
&instruction.data,
|
|
||||||
&mut invoke_context,
|
|
||||||
)?;
|
|
||||||
invoke_context.verify(message, instruction, program_indices)?;
|
|
||||||
timings.accumulate(&invoke_context.timings);
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
.map_err(|err| TransactionError::InstructionError(instruction_index as u8, err));
|
.map_err(|err| TransactionError::InstructionError(instruction_index as u8, err));
|
||||||
invoke_context.pop();
|
|
||||||
|
|
||||||
time.stop();
|
time.stop();
|
||||||
let post_remaining_units = compute_meter.borrow().get_remaining();
|
let post_remaining_units = compute_meter.borrow().get_remaining();
|
||||||
|
|
||||||
timings.accumulate_program(
|
timings.accumulate_program(
|
||||||
instruction.program_id(&message.account_keys),
|
instruction.program_id(&message.account_keys),
|
||||||
time.as_us(),
|
time.as_us(),
|
||||||
pre_remaining_units - post_remaining_units,
|
pre_remaining_units - post_remaining_units,
|
||||||
);
|
);
|
||||||
|
|
||||||
result?;
|
err?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -578,47 +674,6 @@ mod tests {
|
|||||||
process_instruction::MockComputeMeter,
|
process_instruction::MockComputeMeter,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
enum MockInstruction {
|
|
||||||
NoopSuccess,
|
|
||||||
NoopFail,
|
|
||||||
ModifyOwned,
|
|
||||||
ModifyNotOwned,
|
|
||||||
ModifyReadonly,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mock_process_instruction(
|
|
||||||
program_id: &Pubkey,
|
|
||||||
data: &[u8],
|
|
||||||
invoke_context: &mut dyn InvokeContext,
|
|
||||||
) -> Result<(), InstructionError> {
|
|
||||||
let keyed_accounts = invoke_context.get_keyed_accounts()?;
|
|
||||||
assert_eq!(*program_id, keyed_accounts[0].owner()?);
|
|
||||||
assert_ne!(
|
|
||||||
keyed_accounts[1].owner()?,
|
|
||||||
*keyed_accounts[0].unsigned_key()
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Ok(instruction) = bincode::deserialize(data) {
|
|
||||||
match instruction {
|
|
||||||
MockInstruction::NoopSuccess => (),
|
|
||||||
MockInstruction::NoopFail => return Err(InstructionError::GenericError),
|
|
||||||
MockInstruction::ModifyOwned => {
|
|
||||||
keyed_accounts[0].try_account_ref_mut()?.data_as_mut_slice()[0] = 1
|
|
||||||
}
|
|
||||||
MockInstruction::ModifyNotOwned => {
|
|
||||||
keyed_accounts[1].try_account_ref_mut()?.data_as_mut_slice()[0] = 1
|
|
||||||
}
|
|
||||||
MockInstruction::ModifyReadonly => {
|
|
||||||
keyed_accounts[2].try_account_ref_mut()?.data_as_mut_slice()[0] = 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return Err(InstructionError::InvalidInstructionData);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invoke_context() {
|
fn test_invoke_context() {
|
||||||
const MAX_DEPTH: usize = 10;
|
const MAX_DEPTH: usize = 10;
|
||||||
@ -658,7 +713,11 @@ mod tests {
|
|||||||
let blockhash = Hash::default();
|
let blockhash = Hash::default();
|
||||||
let fee_calculator = FeeCalculator::default();
|
let fee_calculator = FeeCalculator::default();
|
||||||
let mut invoke_context = ThisInvokeContext::new(
|
let mut invoke_context = ThisInvokeContext::new(
|
||||||
|
&invoke_stack[0],
|
||||||
Rent::default(),
|
Rent::default(),
|
||||||
|
&message,
|
||||||
|
&message.instructions[0],
|
||||||
|
&[],
|
||||||
&accounts,
|
&accounts,
|
||||||
&[],
|
&[],
|
||||||
None,
|
None,
|
||||||
@ -671,13 +730,20 @@ mod tests {
|
|||||||
&ancestors,
|
&ancestors,
|
||||||
&blockhash,
|
&blockhash,
|
||||||
&fee_calculator,
|
&fee_calculator,
|
||||||
);
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
// Check call depth increases and has a limit
|
// Check call depth increases and has a limit
|
||||||
let mut depth_reached = 0;
|
let mut depth_reached = 1;
|
||||||
for program_id in invoke_stack.iter() {
|
for program_id in invoke_stack.iter().skip(1) {
|
||||||
if Err(InstructionError::CallDepth)
|
if Err(InstructionError::CallDepth)
|
||||||
== invoke_context.push(program_id, &message, &message.instructions[0], &[], None)
|
== invoke_context.push(
|
||||||
|
program_id,
|
||||||
|
&message,
|
||||||
|
&message.instructions[0],
|
||||||
|
&[],
|
||||||
|
&account_indices,
|
||||||
|
)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -740,53 +806,17 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invoke_context_verify() {
|
fn test_verify_account_references() {
|
||||||
let accounts = vec![(
|
let accounts = vec![(
|
||||||
solana_sdk::pubkey::new_rand(),
|
solana_sdk::pubkey::new_rand(),
|
||||||
Rc::new(RefCell::new(AccountSharedData::default())),
|
Rc::new(RefCell::new(AccountSharedData::default())),
|
||||||
)];
|
)];
|
||||||
let message = Message::new(
|
|
||||||
&[Instruction::new_with_bincode(
|
assert!(MessageProcessor::verify_account_references(&accounts, &[0]).is_ok());
|
||||||
accounts[0].0,
|
|
||||||
&MockInstruction::NoopSuccess,
|
|
||||||
vec![AccountMeta::new_readonly(accounts[0].0, false)],
|
|
||||||
)],
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
let ancestors = Ancestors::default();
|
|
||||||
let blockhash = Hash::default();
|
|
||||||
let fee_calculator = FeeCalculator::default();
|
|
||||||
let mut invoke_context = ThisInvokeContext::new(
|
|
||||||
Rent::default(),
|
|
||||||
&accounts,
|
|
||||||
&[],
|
|
||||||
None,
|
|
||||||
ComputeBudget::default(),
|
|
||||||
Rc::new(RefCell::new(MockComputeMeter::default())),
|
|
||||||
Rc::new(RefCell::new(Executors::default())),
|
|
||||||
None,
|
|
||||||
Arc::new(FeatureSet::all_enabled()),
|
|
||||||
Arc::new(Accounts::default_for_tests()),
|
|
||||||
&ancestors,
|
|
||||||
&blockhash,
|
|
||||||
&fee_calculator,
|
|
||||||
);
|
|
||||||
invoke_context
|
|
||||||
.push(
|
|
||||||
&accounts[0].0,
|
|
||||||
&message,
|
|
||||||
&message.instructions[0],
|
|
||||||
&[0],
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
assert!(invoke_context
|
|
||||||
.verify(&message, &message.instructions[0], &[0])
|
|
||||||
.is_ok());
|
|
||||||
|
|
||||||
let mut _borrowed = accounts[0].1.borrow();
|
let mut _borrowed = accounts[0].1.borrow();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
invoke_context.verify(&message, &message.instructions[0], &[0]),
|
MessageProcessor::verify_account_references(&accounts, &[0]),
|
||||||
Err(InstructionError::AccountBorrowOutstanding)
|
Err(InstructionError::AccountBorrowOutstanding)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -833,8 +863,8 @@ mod tests {
|
|||||||
|
|
||||||
let mock_system_program_id = Pubkey::new(&[2u8; 32]);
|
let mock_system_program_id = Pubkey::new(&[2u8; 32]);
|
||||||
let rent_collector = RentCollector::default();
|
let rent_collector = RentCollector::default();
|
||||||
let mut instruction_processor = InstructionProcessor::default();
|
let mut message_processor = MessageProcessor::default();
|
||||||
instruction_processor.add_program(mock_system_program_id, mock_system_process_instruction);
|
message_processor.add_program(mock_system_program_id, mock_system_process_instruction);
|
||||||
|
|
||||||
let program_account = Rc::new(RefCell::new(create_loadable_account_for_test(
|
let program_account = Rc::new(RefCell::new(create_loadable_account_for_test(
|
||||||
"mock_system_program",
|
"mock_system_program",
|
||||||
@ -868,8 +898,7 @@ mod tests {
|
|||||||
Some(&accounts[0].0),
|
Some(&accounts[0].0),
|
||||||
);
|
);
|
||||||
|
|
||||||
let result = MessageProcessor::process_message(
|
let result = message_processor.process_message(
|
||||||
&instruction_processor,
|
|
||||||
&message,
|
&message,
|
||||||
&program_indices,
|
&program_indices,
|
||||||
&accounts,
|
&accounts,
|
||||||
@ -899,8 +928,7 @@ mod tests {
|
|||||||
Some(&accounts[0].0),
|
Some(&accounts[0].0),
|
||||||
);
|
);
|
||||||
|
|
||||||
let result = MessageProcessor::process_message(
|
let result = message_processor.process_message(
|
||||||
&instruction_processor,
|
|
||||||
&message,
|
&message,
|
||||||
&program_indices,
|
&program_indices,
|
||||||
&accounts,
|
&accounts,
|
||||||
@ -934,8 +962,7 @@ mod tests {
|
|||||||
Some(&accounts[0].0),
|
Some(&accounts[0].0),
|
||||||
);
|
);
|
||||||
|
|
||||||
let result = MessageProcessor::process_message(
|
let result = message_processor.process_message(
|
||||||
&instruction_processor,
|
|
||||||
&message,
|
&message,
|
||||||
&program_indices,
|
&program_indices,
|
||||||
&accounts,
|
&accounts,
|
||||||
@ -1024,8 +1051,8 @@ mod tests {
|
|||||||
|
|
||||||
let mock_program_id = Pubkey::new(&[2u8; 32]);
|
let mock_program_id = Pubkey::new(&[2u8; 32]);
|
||||||
let rent_collector = RentCollector::default();
|
let rent_collector = RentCollector::default();
|
||||||
let mut instruction_processor = InstructionProcessor::default();
|
let mut message_processor = MessageProcessor::default();
|
||||||
instruction_processor.add_program(mock_program_id, mock_system_process_instruction);
|
message_processor.add_program(mock_program_id, mock_system_process_instruction);
|
||||||
|
|
||||||
let program_account = Rc::new(RefCell::new(create_loadable_account_for_test(
|
let program_account = Rc::new(RefCell::new(create_loadable_account_for_test(
|
||||||
"mock_system_program",
|
"mock_system_program",
|
||||||
@ -1061,8 +1088,7 @@ mod tests {
|
|||||||
)],
|
)],
|
||||||
Some(&accounts[0].0),
|
Some(&accounts[0].0),
|
||||||
);
|
);
|
||||||
let result = MessageProcessor::process_message(
|
let result = message_processor.process_message(
|
||||||
&instruction_processor,
|
|
||||||
&message,
|
&message,
|
||||||
&program_indices,
|
&program_indices,
|
||||||
&accounts,
|
&accounts,
|
||||||
@ -1096,8 +1122,7 @@ mod tests {
|
|||||||
)],
|
)],
|
||||||
Some(&accounts[0].0),
|
Some(&accounts[0].0),
|
||||||
);
|
);
|
||||||
let result = MessageProcessor::process_message(
|
let result = message_processor.process_message(
|
||||||
&instruction_processor,
|
|
||||||
&message,
|
&message,
|
||||||
&program_indices,
|
&program_indices,
|
||||||
&accounts,
|
&accounts,
|
||||||
@ -1129,8 +1154,7 @@ mod tests {
|
|||||||
Some(&accounts[0].0),
|
Some(&accounts[0].0),
|
||||||
);
|
);
|
||||||
let ancestors = Ancestors::default();
|
let ancestors = Ancestors::default();
|
||||||
let result = MessageProcessor::process_message(
|
let result = message_processor.process_message(
|
||||||
&instruction_processor,
|
|
||||||
&message,
|
&message,
|
||||||
&program_indices,
|
&program_indices,
|
||||||
&accounts,
|
&accounts,
|
||||||
@ -1155,6 +1179,47 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_process_cross_program() {
|
fn test_process_cross_program() {
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
enum MockInstruction {
|
||||||
|
NoopSuccess,
|
||||||
|
NoopFail,
|
||||||
|
ModifyOwned,
|
||||||
|
ModifyNotOwned,
|
||||||
|
ModifyReadonly,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mock_process_instruction(
|
||||||
|
program_id: &Pubkey,
|
||||||
|
data: &[u8],
|
||||||
|
invoke_context: &mut dyn InvokeContext,
|
||||||
|
) -> Result<(), InstructionError> {
|
||||||
|
let keyed_accounts = invoke_context.get_keyed_accounts()?;
|
||||||
|
assert_eq!(*program_id, keyed_accounts[0].owner()?);
|
||||||
|
assert_ne!(
|
||||||
|
keyed_accounts[1].owner()?,
|
||||||
|
*keyed_accounts[0].unsigned_key()
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Ok(instruction) = bincode::deserialize(data) {
|
||||||
|
match instruction {
|
||||||
|
MockInstruction::NoopSuccess => (),
|
||||||
|
MockInstruction::NoopFail => return Err(InstructionError::GenericError),
|
||||||
|
MockInstruction::ModifyOwned => {
|
||||||
|
keyed_accounts[0].try_account_ref_mut()?.data_as_mut_slice()[0] = 1
|
||||||
|
}
|
||||||
|
MockInstruction::ModifyNotOwned => {
|
||||||
|
keyed_accounts[1].try_account_ref_mut()?.data_as_mut_slice()[0] = 1
|
||||||
|
}
|
||||||
|
MockInstruction::ModifyReadonly => {
|
||||||
|
keyed_accounts[2].try_account_ref_mut()?.data_as_mut_slice()[0] = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(InstructionError::InvalidInstructionData);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
let caller_program_id = solana_sdk::pubkey::new_rand();
|
let caller_program_id = solana_sdk::pubkey::new_rand();
|
||||||
let callee_program_id = solana_sdk::pubkey::new_rand();
|
let callee_program_id = solana_sdk::pubkey::new_rand();
|
||||||
|
|
||||||
@ -1164,6 +1229,7 @@ mod tests {
|
|||||||
let mut program_account = AccountSharedData::new(1, 0, &native_loader::id());
|
let mut program_account = AccountSharedData::new(1, 0, &native_loader::id());
|
||||||
program_account.set_executable(true);
|
program_account.set_executable(true);
|
||||||
|
|
||||||
|
#[allow(unused_mut)]
|
||||||
let accounts = vec![
|
let accounts = vec![
|
||||||
(
|
(
|
||||||
solana_sdk::pubkey::new_rand(),
|
solana_sdk::pubkey::new_rand(),
|
||||||
@ -1205,7 +1271,11 @@ mod tests {
|
|||||||
let blockhash = Hash::default();
|
let blockhash = Hash::default();
|
||||||
let fee_calculator = FeeCalculator::default();
|
let fee_calculator = FeeCalculator::default();
|
||||||
let mut invoke_context = ThisInvokeContext::new(
|
let mut invoke_context = ThisInvokeContext::new(
|
||||||
|
&caller_program_id,
|
||||||
Rent::default(),
|
Rent::default(),
|
||||||
|
&message,
|
||||||
|
&caller_instruction,
|
||||||
|
&program_indices,
|
||||||
&accounts,
|
&accounts,
|
||||||
programs.as_slice(),
|
programs.as_slice(),
|
||||||
None,
|
None,
|
||||||
@ -1218,16 +1288,8 @@ mod tests {
|
|||||||
&ancestors,
|
&ancestors,
|
||||||
&blockhash,
|
&blockhash,
|
||||||
&fee_calculator,
|
&fee_calculator,
|
||||||
);
|
)
|
||||||
invoke_context
|
.unwrap();
|
||||||
.push(
|
|
||||||
&caller_program_id,
|
|
||||||
&message,
|
|
||||||
&caller_instruction,
|
|
||||||
&program_indices,
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// not owned account modified by the caller (before the invoke)
|
// not owned account modified by the caller (before the invoke)
|
||||||
let caller_write_privileges = message
|
let caller_write_privileges = message
|
||||||
@ -1285,7 +1347,11 @@ mod tests {
|
|||||||
let blockhash = Hash::default();
|
let blockhash = Hash::default();
|
||||||
let fee_calculator = FeeCalculator::default();
|
let fee_calculator = FeeCalculator::default();
|
||||||
let mut invoke_context = ThisInvokeContext::new(
|
let mut invoke_context = ThisInvokeContext::new(
|
||||||
|
&caller_program_id,
|
||||||
Rent::default(),
|
Rent::default(),
|
||||||
|
&message,
|
||||||
|
&caller_instruction,
|
||||||
|
&program_indices,
|
||||||
&accounts,
|
&accounts,
|
||||||
programs.as_slice(),
|
programs.as_slice(),
|
||||||
None,
|
None,
|
||||||
@ -1298,16 +1364,8 @@ mod tests {
|
|||||||
&ancestors,
|
&ancestors,
|
||||||
&blockhash,
|
&blockhash,
|
||||||
&fee_calculator,
|
&fee_calculator,
|
||||||
);
|
)
|
||||||
invoke_context
|
.unwrap();
|
||||||
.push(
|
|
||||||
&caller_program_id,
|
|
||||||
&message,
|
|
||||||
&caller_instruction,
|
|
||||||
&program_indices,
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let caller_write_privileges = message
|
let caller_write_privileges = message
|
||||||
.account_keys
|
.account_keys
|
||||||
@ -1330,6 +1388,47 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_native_invoke() {
|
fn test_native_invoke() {
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
enum MockInstruction {
|
||||||
|
NoopSuccess,
|
||||||
|
NoopFail,
|
||||||
|
ModifyOwned,
|
||||||
|
ModifyNotOwned,
|
||||||
|
ModifyReadonly,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mock_process_instruction(
|
||||||
|
program_id: &Pubkey,
|
||||||
|
data: &[u8],
|
||||||
|
invoke_context: &mut dyn InvokeContext,
|
||||||
|
) -> Result<(), InstructionError> {
|
||||||
|
let keyed_accounts = invoke_context.get_keyed_accounts()?;
|
||||||
|
assert_eq!(*program_id, keyed_accounts[0].owner()?);
|
||||||
|
assert_ne!(
|
||||||
|
keyed_accounts[1].owner()?,
|
||||||
|
*keyed_accounts[0].unsigned_key()
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Ok(instruction) = bincode::deserialize(data) {
|
||||||
|
match instruction {
|
||||||
|
MockInstruction::NoopSuccess => (),
|
||||||
|
MockInstruction::NoopFail => return Err(InstructionError::GenericError),
|
||||||
|
MockInstruction::ModifyOwned => {
|
||||||
|
keyed_accounts[0].try_account_ref_mut()?.data_as_mut_slice()[0] = 1
|
||||||
|
}
|
||||||
|
MockInstruction::ModifyNotOwned => {
|
||||||
|
keyed_accounts[1].try_account_ref_mut()?.data_as_mut_slice()[0] = 1
|
||||||
|
}
|
||||||
|
MockInstruction::ModifyReadonly => {
|
||||||
|
keyed_accounts[2].try_account_ref_mut()?.data_as_mut_slice()[0] = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(InstructionError::InvalidInstructionData);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
let caller_program_id = solana_sdk::pubkey::new_rand();
|
let caller_program_id = solana_sdk::pubkey::new_rand();
|
||||||
let callee_program_id = solana_sdk::pubkey::new_rand();
|
let callee_program_id = solana_sdk::pubkey::new_rand();
|
||||||
|
|
||||||
@ -1339,6 +1438,7 @@ mod tests {
|
|||||||
let mut program_account = AccountSharedData::new(1, 0, &native_loader::id());
|
let mut program_account = AccountSharedData::new(1, 0, &native_loader::id());
|
||||||
program_account.set_executable(true);
|
program_account.set_executable(true);
|
||||||
|
|
||||||
|
#[allow(unused_mut)]
|
||||||
let accounts = vec![
|
let accounts = vec![
|
||||||
(
|
(
|
||||||
solana_sdk::pubkey::new_rand(),
|
solana_sdk::pubkey::new_rand(),
|
||||||
@ -1376,7 +1476,11 @@ mod tests {
|
|||||||
let blockhash = Hash::default();
|
let blockhash = Hash::default();
|
||||||
let fee_calculator = FeeCalculator::default();
|
let fee_calculator = FeeCalculator::default();
|
||||||
let mut invoke_context = ThisInvokeContext::new(
|
let mut invoke_context = ThisInvokeContext::new(
|
||||||
|
&caller_program_id,
|
||||||
Rent::default(),
|
Rent::default(),
|
||||||
|
&message,
|
||||||
|
&caller_instruction,
|
||||||
|
&program_indices,
|
||||||
&accounts,
|
&accounts,
|
||||||
programs.as_slice(),
|
programs.as_slice(),
|
||||||
None,
|
None,
|
||||||
@ -1389,16 +1493,8 @@ mod tests {
|
|||||||
&ancestors,
|
&ancestors,
|
||||||
&blockhash,
|
&blockhash,
|
||||||
&fee_calculator,
|
&fee_calculator,
|
||||||
);
|
)
|
||||||
invoke_context
|
.unwrap();
|
||||||
.push(
|
|
||||||
&caller_program_id,
|
|
||||||
&message,
|
|
||||||
&caller_instruction,
|
|
||||||
&program_indices,
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// not owned account modified by the invoker
|
// not owned account modified by the invoker
|
||||||
accounts[0].1.borrow_mut().data_as_mut_slice()[0] = 1;
|
accounts[0].1.borrow_mut().data_as_mut_slice()[0] = 1;
|
||||||
@ -1452,7 +1548,11 @@ mod tests {
|
|||||||
let blockhash = Hash::default();
|
let blockhash = Hash::default();
|
||||||
let fee_calculator = FeeCalculator::default();
|
let fee_calculator = FeeCalculator::default();
|
||||||
let mut invoke_context = ThisInvokeContext::new(
|
let mut invoke_context = ThisInvokeContext::new(
|
||||||
|
&caller_program_id,
|
||||||
Rent::default(),
|
Rent::default(),
|
||||||
|
&message,
|
||||||
|
&caller_instruction,
|
||||||
|
&program_indices,
|
||||||
&accounts,
|
&accounts,
|
||||||
programs.as_slice(),
|
programs.as_slice(),
|
||||||
None,
|
None,
|
||||||
@ -1465,16 +1565,8 @@ mod tests {
|
|||||||
&ancestors,
|
&ancestors,
|
||||||
&blockhash,
|
&blockhash,
|
||||||
&fee_calculator,
|
&fee_calculator,
|
||||||
);
|
)
|
||||||
invoke_context
|
.unwrap();
|
||||||
.push(
|
|
||||||
&caller_program_id,
|
|
||||||
&message,
|
|
||||||
&caller_instruction,
|
|
||||||
&program_indices,
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
InstructionProcessor::native_invoke(
|
InstructionProcessor::native_invoke(
|
||||||
|
@ -58,19 +58,12 @@ pub trait InvokeContext {
|
|||||||
message: &Message,
|
message: &Message,
|
||||||
instruction: &CompiledInstruction,
|
instruction: &CompiledInstruction,
|
||||||
program_indices: &[usize],
|
program_indices: &[usize],
|
||||||
account_indices: Option<&[usize]>,
|
account_indices: &[usize],
|
||||||
) -> Result<(), InstructionError>;
|
) -> Result<(), InstructionError>;
|
||||||
/// Pop a stack frame from the invocation stack
|
/// Pop a stack frame from the invocation stack
|
||||||
fn pop(&mut self);
|
fn pop(&mut self);
|
||||||
/// Current depth of the invocation stake
|
/// Current depth of the invocation stake
|
||||||
fn invoke_depth(&self) -> usize;
|
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
|
/// Verify and update PreAccount state based on program execution
|
||||||
fn verify_and_update(
|
fn verify_and_update(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -99,8 +92,6 @@ pub trait InvokeContext {
|
|||||||
fn add_executor(&self, pubkey: &Pubkey, executor: Arc<dyn Executor>);
|
fn add_executor(&self, pubkey: &Pubkey, executor: Arc<dyn Executor>);
|
||||||
/// Get the completed loader work that can be re-used across executions
|
/// Get the completed loader work that can be re-used across executions
|
||||||
fn get_executor(&self, pubkey: &Pubkey) -> Option<Arc<dyn Executor>>;
|
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
|
/// Record invoked instruction
|
||||||
fn record_instruction(&self, instruction: &Instruction);
|
fn record_instruction(&self, instruction: &Instruction);
|
||||||
/// Get the bank's active feature set
|
/// Get the bank's active feature set
|
||||||
@ -501,7 +492,7 @@ impl<'a> InvokeContext for MockInvokeContext<'a> {
|
|||||||
_message: &Message,
|
_message: &Message,
|
||||||
_instruction: &CompiledInstruction,
|
_instruction: &CompiledInstruction,
|
||||||
_program_indices: &[usize],
|
_program_indices: &[usize],
|
||||||
_account_indices: Option<&[usize]>,
|
_account_indices: &[usize],
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
self.invoke_stack.push(InvokeContextStackFrame::new(
|
self.invoke_stack.push(InvokeContextStackFrame::new(
|
||||||
*_key,
|
*_key,
|
||||||
@ -515,14 +506,6 @@ impl<'a> InvokeContext for MockInvokeContext<'a> {
|
|||||||
fn invoke_depth(&self) -> usize {
|
fn invoke_depth(&self) -> usize {
|
||||||
self.invoke_stack.len()
|
self.invoke_stack.len()
|
||||||
}
|
}
|
||||||
fn verify(
|
|
||||||
&mut self,
|
|
||||||
_message: &Message,
|
|
||||||
_instruction: &CompiledInstruction,
|
|
||||||
_program_indices: &[usize],
|
|
||||||
) -> Result<(), InstructionError> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn verify_and_update(
|
fn verify_and_update(
|
||||||
&mut self,
|
&mut self,
|
||||||
_instruction: &CompiledInstruction,
|
_instruction: &CompiledInstruction,
|
||||||
@ -570,7 +553,6 @@ impl<'a> InvokeContext for MockInvokeContext<'a> {
|
|||||||
fn get_executor(&self, _pubkey: &Pubkey) -> Option<Arc<dyn Executor>> {
|
fn get_executor(&self, _pubkey: &Pubkey) -> Option<Arc<dyn Executor>> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
fn set_instruction_index(&mut self, _instruction_index: usize) {}
|
|
||||||
fn record_instruction(&self, _instruction: &Instruction) {}
|
fn record_instruction(&self, _instruction: &Instruction) {}
|
||||||
fn is_feature_active(&self, feature_id: &Pubkey) -> bool {
|
fn is_feature_active(&self, feature_id: &Pubkey) -> bool {
|
||||||
!self.disabled_features.contains(feature_id)
|
!self.disabled_features.contains(feature_id)
|
||||||
|
Reference in New Issue
Block a user