solana/runtime/src/message_processor.rs

2349 lines
85 KiB
Rust
Raw Normal View History

use crate::{
2021-04-12 16:04:57 -07:00
accounts::Accounts, accounts_index::Ancestors, instruction_recorder::InstructionRecorder,
log_collector::LogCollector, native_loader::NativeLoader, rent_collector::RentCollector,
};
use log::*;
use serde::{Deserialize, Serialize};
2020-01-28 17:03:20 -08:00
use solana_sdk::{
2021-03-10 13:28:03 -08:00
account::{AccountSharedData, ReadableAccount, WritableAccount},
account_utils::StateMut,
bpf_loader_upgradeable::{self, UpgradeableLoaderState},
feature_set::{
cpi_share_ro_and_exec_accounts, demote_sysvar_write_locks, instructions_sysvar_enabled,
FeatureSet,
},
ic_msg,
instruction::{CompiledInstruction, Instruction, InstructionError},
keyed_account::{create_keyed_accounts_unified, keyed_account_at_index, KeyedAccount},
2020-01-28 17:03:20 -08:00
message::Message,
2020-04-15 09:41:29 -07:00
native_loader,
process_instruction::{
BpfComputeBudget, ComputeMeter, Executor, InvokeContext, InvokeContextStackFrame, Logger,
2020-10-28 13:16:13 -07:00
ProcessInstructionWithContext,
},
2020-01-28 17:03:20 -08:00
pubkey::Pubkey,
2020-04-28 14:33:56 -07:00
rent::Rent,
2020-01-28 17:03:20 -08:00
system_program,
sysvar::instructions,
2020-01-28 17:03:20 -08:00
transaction::TransactionError,
};
use std::{
cell::{Ref, RefCell},
collections::HashMap,
rc::Rc,
sync::Arc,
};
pub struct Executors {
pub executors: HashMap<Pubkey, Arc<dyn Executor>>,
pub is_dirty: bool,
}
impl Default for Executors {
fn default() -> Self {
Self {
executors: HashMap::default(),
is_dirty: false,
}
}
}
impl Executors {
pub fn insert(&mut self, key: Pubkey, executor: Arc<dyn Executor>) {
let _ = self.executors.insert(key, executor);
self.is_dirty = true;
}
pub fn get(&self, key: &Pubkey) -> Option<Arc<dyn Executor>> {
self.executors.get(key).cloned()
}
}
2021-03-03 15:07:45 -08:00
#[derive(Default, Debug)]
pub struct ExecuteDetailsTimings {
pub serialize_us: u64,
pub create_vm_us: u64,
pub execute_us: u64,
pub deserialize_us: u64,
pub changed_account_count: u64,
pub total_account_count: u64,
pub total_data_size: usize,
pub data_size_changed: usize,
}
impl ExecuteDetailsTimings {
pub fn accumulate(&mut self, other: &ExecuteDetailsTimings) {
self.serialize_us += other.serialize_us;
self.create_vm_us += other.create_vm_us;
self.execute_us += other.execute_us;
self.deserialize_us += other.deserialize_us;
self.changed_account_count += other.changed_account_count;
self.total_account_count += other.total_account_count;
self.total_data_size += other.total_data_size;
self.data_size_changed += other.data_size_changed;
}
}
// The relevant state of an account before an Instruction executes, used
// to verify account integrity after the Instruction completes
2020-04-28 14:33:56 -07:00
#[derive(Clone, Debug, Default)]
pub struct PreAccount {
2020-04-28 14:33:56 -07:00
key: Pubkey,
account: Rc<RefCell<AccountSharedData>>,
2021-03-03 15:07:45 -08:00
changed: bool,
}
impl PreAccount {
2021-03-10 09:48:41 -08:00
pub fn new(key: &Pubkey, account: &AccountSharedData) -> Self {
Self {
2020-04-28 14:33:56 -07:00
key: *key,
account: Rc::new(RefCell::new(account.clone())),
2021-03-03 15:07:45 -08:00
changed: false,
}
}
pub fn verify(
&self,
program_id: &Pubkey,
2021-03-10 09:48:41 -08:00
is_writable: bool,
2020-04-28 14:33:56 -07:00
rent: &Rent,
2021-03-09 13:06:07 -08:00
post: &AccountSharedData,
2021-03-03 15:07:45 -08:00
timings: &mut ExecuteDetailsTimings,
outermost_call: bool,
) -> Result<(), InstructionError> {
let pre = self.account.borrow();
// Only the owner of the account may change owner and
// only if the account is writable and
// only if the account is not executable and
// only if the data is zero-initialized or empty
2021-04-26 10:06:40 -07:00
let owner_changed = pre.owner() != post.owner();
2021-03-03 15:07:45 -08:00
if owner_changed
&& (!is_writable // line coverage used to get branch coverage
|| pre.executable()
2021-04-26 10:06:40 -07:00
|| program_id != pre.owner()
2021-03-09 14:31:33 -08:00
|| !Self::is_zeroed(&post.data()))
{
return Err(InstructionError::ModifiedProgramId);
}
// An account not assigned to the program cannot have its balance decrease.
2021-04-26 10:06:40 -07:00
if program_id != pre.owner() // line coverage used to get branch coverage
2021-04-29 08:44:46 -07:00
&& pre.lamports() > post.lamports()
{
return Err(InstructionError::ExternalAccountLamportSpend);
}
// The balance of read-only and executable accounts may not change
2021-04-29 08:44:46 -07:00
let lamports_changed = pre.lamports() != post.lamports();
2021-03-03 15:07:45 -08:00
if lamports_changed {
if !is_writable {
return Err(InstructionError::ReadonlyLamportChange);
}
if pre.executable() {
return Err(InstructionError::ExecutableLamportChange);
}
}
// Only the system program can change the size of the data
// and only if the system program owns the account
2021-03-09 14:31:33 -08:00
let data_len_changed = pre.data().len() != post.data().len();
2021-03-03 15:07:45 -08:00
if data_len_changed
&& (!system_program::check_id(program_id) // line coverage used to get branch coverage
2021-04-26 10:06:40 -07:00
|| !system_program::check_id(pre.owner()))
{
return Err(InstructionError::AccountDataSizeChanged);
}
2020-04-28 14:33:56 -07:00
// Only the owner may change account data
// and if the account is writable
// and if the account is not executable
2021-04-26 10:06:40 -07:00
if !(program_id == pre.owner()
&& is_writable // line coverage used to get branch coverage
&& !pre.executable())
2021-03-09 14:31:33 -08:00
&& pre.data() != post.data()
2020-04-28 14:33:56 -07:00
{
if pre.executable() {
2020-04-28 14:33:56 -07:00
return Err(InstructionError::ExecutableDataModified);
} else if is_writable {
2020-04-28 14:33:56 -07:00
return Err(InstructionError::ExternalAccountDataModified);
} else {
return Err(InstructionError::ReadonlyDataModified);
2019-10-25 21:47:16 -07:00
}
}
// executable is one-way (false->true) and only the account owner may set it.
let executable_changed = pre.executable() != post.executable();
2021-03-03 15:07:45 -08:00
if executable_changed {
2021-04-29 08:44:46 -07:00
if !rent.is_exempt(post.lamports(), post.data().len()) {
return Err(InstructionError::ExecutableAccountNotRentExempt);
}
if !is_writable // line coverage used to get branch coverage
|| pre.executable()
2021-04-26 10:06:40 -07:00
|| program_id != pre.owner()
{
return Err(InstructionError::ExecutableModified);
}
}
// No one modifies rent_epoch (yet).
let rent_epoch_changed = pre.rent_epoch() != post.rent_epoch();
2021-03-03 15:07:45 -08:00
if rent_epoch_changed {
return Err(InstructionError::RentEpochModified);
}
if outermost_call {
timings.total_account_count += 1;
timings.total_data_size += post.data().len();
if owner_changed
|| lamports_changed
|| data_len_changed
|| executable_changed
|| rent_epoch_changed
|| self.changed
{
timings.changed_account_count += 1;
timings.data_size_changed += post.data().len();
}
2021-03-03 15:07:45 -08:00
}
Ok(())
}
2021-03-09 13:06:07 -08:00
pub fn update(&mut self, account: &AccountSharedData) {
let mut pre = self.account.borrow_mut();
let rent_epoch = pre.rent_epoch();
*pre = account.clone();
pre.set_rent_epoch(rent_epoch);
2021-03-03 15:07:45 -08:00
self.changed = true;
2020-04-28 14:33:56 -07:00
}
pub fn key(&self) -> &Pubkey {
&self.key
2020-04-28 14:33:56 -07:00
}
pub fn lamports(&self) -> u64 {
2021-04-29 08:44:46 -07:00
self.account.borrow().lamports()
2020-04-28 14:33:56 -07:00
}
pub fn is_zeroed(buf: &[u8]) -> bool {
const ZEROS_LEN: usize = 1024;
static ZEROS: [u8; ZEROS_LEN] = [0; ZEROS_LEN];
let mut chunks = buf.chunks_exact(ZEROS_LEN);
chunks.all(|chunk| chunk == &ZEROS[..])
&& chunks.remainder() == &ZEROS[..chunks.remainder().len()]
}
}
pub struct ThisComputeMeter {
remaining: u64,
}
impl ComputeMeter for ThisComputeMeter {
fn consume(&mut self, amount: u64) -> Result<(), InstructionError> {
let exceeded = self.remaining < amount;
self.remaining = self.remaining.saturating_sub(amount);
if exceeded {
return Err(InstructionError::ComputationalBudgetExceeded);
}
Ok(())
}
fn get_remaining(&self) -> u64 {
self.remaining
}
}
2020-10-29 09:57:14 -07:00
pub struct ThisInvokeContext<'a> {
invoke_stack: Vec<InvokeContextStackFrame<'a>>,
rent: Rent,
message: &'a Message,
pre_accounts: Vec<PreAccount>,
executable_accounts: &'a [(Pubkey, Rc<RefCell<AccountSharedData>>)],
account_deps: &'a [(Pubkey, Rc<RefCell<AccountSharedData>>)],
2020-10-29 09:57:14 -07:00
programs: &'a [(Pubkey, ProcessInstructionWithContext)],
logger: Rc<RefCell<dyn Logger>>,
2020-10-28 13:16:13 -07:00
bpf_compute_budget: BpfComputeBudget,
compute_meter: Rc<RefCell<dyn ComputeMeter>>,
executors: Rc<RefCell<Executors>>,
instruction_recorder: Option<InstructionRecorder>,
feature_set: Arc<FeatureSet>,
2021-03-03 15:07:45 -08:00
pub timings: ExecuteDetailsTimings,
2021-04-12 16:04:57 -07:00
account_db: Arc<Accounts>,
ancestors: &'a Ancestors,
sysvars: Vec<(Pubkey, Option<Rc<Vec<u8>>>)>,
2020-04-28 14:33:56 -07:00
}
2020-10-29 09:57:14 -07:00
impl<'a> ThisInvokeContext<'a> {
#[allow(clippy::too_many_arguments)]
pub fn new(
program_id: &Pubkey,
rent: Rent,
message: &'a Message,
instruction: &'a CompiledInstruction,
executable_accounts: &'a [(Pubkey, Rc<RefCell<AccountSharedData>>)],
accounts: &'a [Rc<RefCell<AccountSharedData>>],
account_deps: &'a [(Pubkey, Rc<RefCell<AccountSharedData>>)],
2020-10-29 09:57:14 -07:00
programs: &'a [(Pubkey, ProcessInstructionWithContext)],
log_collector: Option<Rc<LogCollector>>,
2020-10-28 13:16:13 -07:00
bpf_compute_budget: BpfComputeBudget,
executors: Rc<RefCell<Executors>>,
instruction_recorder: Option<InstructionRecorder>,
feature_set: Arc<FeatureSet>,
2021-04-12 16:04:57 -07:00
account_db: Arc<Accounts>,
ancestors: &'a Ancestors,
) -> Self {
let pre_accounts = MessageProcessor::create_pre_accounts(message, instruction, accounts);
let keyed_accounts = MessageProcessor::create_keyed_accounts(
message,
instruction,
executable_accounts,
accounts,
feature_set.is_active(&demote_sysvar_write_locks::id()),
);
let mut invoke_context = Self {
invoke_stack: Vec::with_capacity(bpf_compute_budget.max_invoke_depth),
2020-04-28 14:33:56 -07:00
rent,
message,
2020-04-28 14:33:56 -07:00
pre_accounts,
executable_accounts,
account_deps,
programs,
logger: Rc::new(RefCell::new(ThisLogger { log_collector })),
2020-10-28 13:16:13 -07:00
bpf_compute_budget,
compute_meter: Rc::new(RefCell::new(ThisComputeMeter {
2020-10-28 13:16:13 -07:00
remaining: bpf_compute_budget.max_units,
})),
executors,
instruction_recorder,
feature_set,
2021-03-03 15:07:45 -08:00
timings: ExecuteDetailsTimings::default(),
2021-04-12 16:04:57 -07:00
account_db,
ancestors,
sysvars: vec![],
};
invoke_context
.invoke_stack
.push(InvokeContextStackFrame::new(
*program_id,
create_keyed_accounts_unified(&keyed_accounts),
));
invoke_context
2020-04-28 14:33:56 -07:00
}
}
2020-10-29 09:57:14 -07:00
impl<'a> InvokeContext for ThisInvokeContext<'a> {
fn push(
&mut self,
key: &Pubkey,
keyed_accounts: &[(bool, bool, &Pubkey, &RefCell<AccountSharedData>)],
) -> Result<(), InstructionError> {
if self.invoke_stack.len() > self.bpf_compute_budget.max_invoke_depth {
2020-04-28 14:33:56 -07:00
return Err(InstructionError::CallDepth);
}
let frame_index = self.invoke_stack.iter().position(|frame| frame.key == *key);
if frame_index != None && frame_index != Some(self.invoke_stack.len().saturating_sub(1)) {
2020-04-28 14:33:56 -07:00
// Reentrancy not allowed unless caller is calling itself
return Err(InstructionError::ReentrancyNotAllowed);
}
// Alias the keys and account references in the provided keyed_accounts
// with the ones already existing in self, so that the lifetime 'a matches.
fn transmute_lifetime<'a, 'b, T: Sized>(value: &'a T) -> &'b T {
unsafe { std::mem::transmute(value) }
}
let keyed_accounts = keyed_accounts
.iter()
.map(|(is_signer, is_writable, search_key, account)| {
self.account_deps
.iter()
.map(|(key, _account)| key)
.chain(self.message.account_keys.iter())
.position(|key| key == *search_key)
.map(|mut index| {
if index < self.account_deps.len() {
(
*is_signer,
*is_writable,
&self.account_deps[index].0,
&self.account_deps[index].1 as &RefCell<AccountSharedData>,
)
} else {
index = index.saturating_sub(self.account_deps.len());
(
*is_signer,
*is_writable,
&self.message.account_keys[index],
// TODO
// Currently we are constructing new accounts on the stack
// before calling MessageProcessor::process_cross_program_instruction
// Ideally we would recycle the existing accounts here like this:
// &self.accounts[index] as &RefCell<AccountSharedData>,
transmute_lifetime(*account),
)
}
})
})
.collect::<Option<Vec<_>>>()
.ok_or(InstructionError::InvalidArgument)?;
self.invoke_stack.push(InvokeContextStackFrame::new(
*key,
create_keyed_accounts_unified(keyed_accounts.as_slice()),
));
2020-04-28 14:33:56 -07:00
Ok(())
}
fn pop(&mut self) {
self.invoke_stack.pop();
2020-04-28 14:33:56 -07:00
}
fn invoke_depth(&self) -> usize {
self.invoke_stack.len()
}
2020-04-28 14:33:56 -07:00
fn verify_and_update(
&mut self,
message: &Message,
instruction: &CompiledInstruction,
2021-03-09 13:06:07 -08:00
accounts: &[Rc<RefCell<AccountSharedData>>],
caller_write_privileges: Option<&[bool]>,
2020-04-28 14:33:56 -07:00
) -> Result<(), InstructionError> {
let stack_frame = self
.invoke_stack
.last()
.ok_or(InstructionError::CallDepth)?;
MessageProcessor::verify_and_update(
message,
instruction,
&mut self.pre_accounts,
accounts,
&stack_frame.key,
&self.rent,
caller_write_privileges,
&mut self.timings,
self.feature_set.is_active(&demote_sysvar_write_locks::id()),
)
2020-04-28 14:33:56 -07:00
}
2020-05-08 12:24:36 -07:00
fn get_caller(&self) -> Result<&Pubkey, InstructionError> {
self.invoke_stack
2020-05-08 12:24:36 -07:00
.last()
.map(|frame| &frame.key)
.ok_or(InstructionError::CallDepth)
}
fn remove_first_keyed_account(&mut self) -> Result<(), InstructionError> {
let stack_frame = &mut self
.invoke_stack
.last_mut()
.ok_or(InstructionError::CallDepth)?;
stack_frame.keyed_accounts_range.start =
stack_frame.keyed_accounts_range.start.saturating_add(1);
Ok(())
}
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)
2020-05-08 12:24:36 -07:00
}
fn get_programs(&self) -> &[(Pubkey, ProcessInstructionWithContext)] {
2020-10-29 09:57:14 -07:00
self.programs
}
fn get_logger(&self) -> Rc<RefCell<dyn Logger>> {
self.logger.clone()
}
2020-10-28 13:16:13 -07:00
fn get_bpf_compute_budget(&self) -> &BpfComputeBudget {
&self.bpf_compute_budget
}
fn get_compute_meter(&self) -> Rc<RefCell<dyn ComputeMeter>> {
self.compute_meter.clone()
}
2020-10-29 23:43:10 -07:00
fn add_executor(&self, pubkey: &Pubkey, executor: Arc<dyn Executor>) {
self.executors.borrow_mut().insert(*pubkey, executor);
}
2020-10-29 23:43:10 -07:00
fn get_executor(&self, pubkey: &Pubkey) -> Option<Arc<dyn Executor>> {
self.executors.borrow().get(&pubkey)
}
fn record_instruction(&self, instruction: &Instruction) {
if let Some(recorder) = &self.instruction_recorder {
recorder.record_instruction(instruction.clone());
}
}
fn is_feature_active(&self, feature_id: &Pubkey) -> bool {
self.feature_set.is_active(feature_id)
}
fn get_account(&self, pubkey: &Pubkey) -> Option<Rc<RefCell<AccountSharedData>>> {
if self.is_feature_active(&cpi_share_ro_and_exec_accounts::id()) {
if let Some((_, account)) = self
.executable_accounts
.iter()
.find(|(key, _)| key == pubkey)
{
Some(account.clone())
} else if let Some((_, account)) =
self.account_deps.iter().find(|(key, _)| key == pubkey)
{
Some(account.clone())
} else {
self.pre_accounts
.iter()
.find(|pre| pre.key == *pubkey)
.map(|pre| pre.account.clone())
}
} else {
if let Some(account) = self.pre_accounts.iter().find_map(|pre| {
if pre.key == *pubkey {
Some(pre.account.clone())
} else {
None
}
}) {
return Some(account);
}
self.account_deps.iter().find_map(|(key, account)| {
if key == pubkey {
Some(account.clone())
} else {
None
}
})
}
}
2021-03-03 15:07:45 -08:00
fn update_timing(
&mut self,
serialize_us: u64,
create_vm_us: u64,
execute_us: u64,
deserialize_us: u64,
) {
self.timings.serialize_us += serialize_us;
self.timings.create_vm_us += create_vm_us;
self.timings.execute_us += execute_us;
self.timings.deserialize_us += deserialize_us;
}
2021-04-12 16:04:57 -07:00
fn get_sysvar_data(&mut self, id: &Pubkey) -> Option<Rc<Vec<u8>>> {
// Try share from cache
let mut result =
self.sysvars
.iter()
.find_map(|(key, sysvar)| if id == key { sysvar.clone() } else { None });
if result.is_none() {
// Load it
result = self
.account_db
.load_with_fixed_root(self.ancestors, id)
.map(|(account, _)| Rc::new(account.data().to_vec()));
2021-04-12 16:04:57 -07:00
// Cache it
self.sysvars.push((*id, result.clone()));
}
result
}
}
pub struct ThisLogger {
log_collector: Option<Rc<LogCollector>>,
}
impl Logger for ThisLogger {
fn log_enabled(&self) -> bool {
log_enabled!(log::Level::Info) || self.log_collector.is_some()
}
2020-10-29 23:43:10 -07:00
fn log(&self, message: &str) {
debug!("{}", message);
if let Some(log_collector) = &self.log_collector {
log_collector.log(message);
}
}
2020-04-28 14:33:56 -07:00
}
#[derive(Deserialize, Serialize)]
2019-04-02 08:35:38 -07:00
pub struct MessageProcessor {
#[serde(skip)]
programs: Vec<(Pubkey, ProcessInstructionWithContext)>,
#[serde(skip)]
2020-04-15 09:41:29 -07:00
native_loader: NativeLoader,
}
impl std::fmt::Debug for MessageProcessor {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
#[derive(Debug)]
struct MessageProcessor<'a> {
programs: Vec<String>,
native_loader: &'a NativeLoader,
}
2020-10-29 16:17:36 -07:00
// These are just type aliases for work around of Debug-ing above pointers
type ErasedProcessInstructionWithContext = fn(
&'static Pubkey,
&'static [u8],
&'static mut dyn InvokeContext,
) -> Result<(), InstructionError>;
// rustc doesn't compile due to bug without this work around
// https://github.com/rust-lang/rust/issues/50280
// https://users.rust-lang.org/t/display-function-pointer/17073/2
let processor = MessageProcessor {
programs: self
.programs
.iter()
.map(|(pubkey, instruction)| {
let erased_instruction: ErasedProcessInstructionWithContext = *instruction;
format!("{}: {:p}", pubkey, erased_instruction)
})
.collect::<Vec<_>>(),
native_loader: &self.native_loader,
};
write!(f, "{:?}", processor)
}
}
impl Default for MessageProcessor {
fn default() -> Self {
Self {
programs: vec![],
native_loader: NativeLoader::default(),
}
}
}
2020-04-27 21:05:12 -07:00
impl Clone for MessageProcessor {
fn clone(&self) -> Self {
MessageProcessor {
programs: self.programs.clone(),
2020-04-15 09:41:29 -07:00
native_loader: NativeLoader::default(),
}
}
}
#[cfg(RUSTC_WITH_SPECIALIZATION)]
impl ::solana_frozen_abi::abi_example::AbiExample for MessageProcessor {
fn example() -> Self {
// MessageProcessor's fields are #[serde(skip)]-ed and not Serialize
// so, just rely on Default anyway.
MessageProcessor::default()
}
}
2019-04-02 08:35:38 -07:00
impl MessageProcessor {
/// Add a static entrypoint to intercept instructions before the dynamic loader.
pub fn add_program(
&mut self,
program_id: Pubkey,
process_instruction: ProcessInstructionWithContext,
) {
match self.programs.iter_mut().find(|(key, _)| program_id == *key) {
2020-04-27 21:05:12 -07:00
Some((_, processor)) => *processor = process_instruction,
None => self.programs.push((program_id, process_instruction)),
2020-04-27 21:05:12 -07:00
}
}
pub fn add_loader(
&mut self,
program_id: Pubkey,
process_instruction: ProcessInstructionWithContext,
) {
2020-10-29 14:15:00 -07:00
self.add_program(program_id, process_instruction);
}
2020-04-28 14:33:56 -07:00
/// Create the KeyedAccounts that will be passed to the program
fn create_keyed_accounts<'a>(
message: &'a Message,
instruction: &'a CompiledInstruction,
executable_accounts: &'a [(Pubkey, Rc<RefCell<AccountSharedData>>)],
2021-03-09 13:06:07 -08:00
accounts: &'a [Rc<RefCell<AccountSharedData>>],
demote_sysvar_write_locks: bool,
) -> Vec<(bool, bool, &'a Pubkey, &'a RefCell<AccountSharedData>)> {
executable_accounts
.iter()
.map(|(key, account)| (false, false, key, account as &RefCell<AccountSharedData>))
.chain(instruction.accounts.iter().map(|index| {
let index = *index as usize;
(
message.is_signer(index),
message.is_writable(index, demote_sysvar_write_locks),
&message.account_keys[index],
&accounts[index] as &RefCell<AccountSharedData>,
)
}))
.collect::<Vec<_>>()
2020-04-28 14:33:56 -07:00
}
/// Process an instruction
/// This method calls the instruction's program entrypoint method
fn process_instruction(
&self,
program_id: &Pubkey,
instruction_data: &[u8],
2020-04-28 14:33:56 -07:00
invoke_context: &mut dyn InvokeContext,
) -> Result<(), InstructionError> {
if let Some(root_account) = invoke_context.get_keyed_accounts()?.iter().next() {
let root_id = root_account.unsigned_key();
if native_loader::check_id(&root_account.owner()?) {
for (id, process_instruction) in &self.programs {
if id == root_id {
invoke_context.remove_first_keyed_account()?;
// Call the builtin program
return process_instruction(&program_id, instruction_data, invoke_context);
}
}
// Call the program via the native loader
return self.native_loader.process_instruction(
2021-01-11 14:36:52 -08:00
&native_loader::id(),
instruction_data,
invoke_context,
);
} else {
let owner_id = &root_account.owner()?;
2020-10-29 14:15:00 -07:00
for (id, process_instruction) in &self.programs {
if id == owner_id {
// Call the program via a builtin loader
return process_instruction(&program_id, instruction_data, invoke_context);
}
}
}
2020-04-15 09:41:29 -07:00
}
Err(InstructionError::UnsupportedProgramId)
}
pub fn create_message(
instruction: &Instruction,
keyed_accounts: &[&KeyedAccount],
signers: &[Pubkey],
invoke_context: &Ref<&mut dyn InvokeContext>,
) -> Result<(Message, Pubkey, usize), InstructionError> {
// Check for privilege escalation
for account in instruction.accounts.iter() {
let keyed_account = keyed_accounts
.iter()
.find_map(|keyed_account| {
if &account.pubkey == keyed_account.unsigned_key() {
Some(keyed_account)
} else {
None
}
})
.ok_or_else(|| {
ic_msg!(
invoke_context,
2021-02-01 11:40:49 -08:00
"Instruction references an unknown account {}",
account.pubkey
);
InstructionError::MissingAccount
})?;
// Readonly account cannot become writable
if account.is_writable && !keyed_account.is_writable() {
ic_msg!(
invoke_context,
2021-02-01 11:40:49 -08:00
"{}'s writable privilege escalated",
account.pubkey
);
return Err(InstructionError::PrivilegeEscalation);
}
if account.is_signer && // If message indicates account is signed
!( // one of the following needs to be true:
keyed_account.signer_key().is_some() // Signed in the parent instruction
|| signers.contains(&account.pubkey) // Signed by the program
) {
ic_msg!(
invoke_context,
2021-03-04 23:16:53 -08:00
"{}'s signer privilege escalated",
account.pubkey
);
return Err(InstructionError::PrivilegeEscalation);
}
}
// validate the caller has access to the program account and that it is executable
let program_id = instruction.program_id;
match keyed_accounts
.iter()
.find(|keyed_account| &program_id == keyed_account.unsigned_key())
{
Some(keyed_account) => {
if !keyed_account.executable()? {
2021-02-01 11:40:49 -08:00
ic_msg!(
invoke_context,
"Account {} is not executable",
keyed_account.unsigned_key()
);
return Err(InstructionError::AccountNotExecutable);
}
}
None => {
2021-02-01 11:40:49 -08:00
ic_msg!(invoke_context, "Unknown program {}", program_id);
return Err(InstructionError::MissingAccount);
}
}
let message = Message::new(&[instruction.clone()], None);
let program_id_index = message.instructions[0].program_id_index as usize;
Ok((message, program_id, program_id_index))
}
2020-12-14 15:35:10 -08:00
/// Entrypoint for a cross-program invocation from a native program
pub fn native_invoke(
invoke_context: &mut dyn InvokeContext,
instruction: Instruction,
keyed_account_indices: &[usize],
signers: &[Pubkey],
2020-12-14 15:35:10 -08:00
) -> Result<(), InstructionError> {
let invoke_context = RefCell::new(invoke_context);
2020-12-14 15:35:10 -08:00
let (
message,
executable_accounts,
accounts,
keyed_account_indices_reordered,
caller_write_privileges,
demote_sysvar_write_locks,
) = {
let invoke_context = invoke_context.borrow();
2020-12-14 15:35:10 -08:00
// Translate and verify caller's data
let keyed_accounts = invoke_context.get_keyed_accounts()?;
let keyed_accounts = keyed_account_indices
.iter()
.map(|index| keyed_account_at_index(keyed_accounts, *index))
.collect::<Result<Vec<&KeyedAccount>, InstructionError>>()?;
let (message, callee_program_id, _) =
Self::create_message(&instruction, &keyed_accounts, &signers, &invoke_context)?;
let keyed_accounts = invoke_context.get_keyed_accounts()?;
let mut caller_write_privileges = keyed_account_indices
.iter()
.map(|index| keyed_accounts[*index].is_writable())
.collect::<Vec<bool>>();
caller_write_privileges.insert(0, false);
let mut accounts = vec![];
let mut keyed_account_indices_reordered = vec![];
let keyed_accounts = invoke_context.get_keyed_accounts()?;
'root: for account_key in message.account_keys.iter() {
for keyed_account_index in keyed_account_indices {
let keyed_account = &keyed_accounts[*keyed_account_index];
if account_key == keyed_account.unsigned_key() {
accounts.push(Rc::new(keyed_account.account.clone()));
keyed_account_indices_reordered.push(*keyed_account_index);
continue 'root;
}
2020-12-14 15:35:10 -08:00
}
ic_msg!(
invoke_context,
2021-02-01 11:40:49 -08:00
"Instruction references an unknown account {}",
account_key
);
return Err(InstructionError::MissingAccount);
2020-12-14 15:35:10 -08:00
}
// Process instruction
2020-12-14 15:35:10 -08:00
invoke_context.record_instruction(&instruction);
let program_account =
invoke_context
.get_account(&callee_program_id)
.ok_or_else(|| {
2021-02-01 11:40:49 -08:00
ic_msg!(invoke_context, "Unknown program {}", callee_program_id);
InstructionError::MissingAccount
})?;
if !program_account.borrow().executable() {
2021-02-01 11:40:49 -08:00
ic_msg!(
invoke_context,
"Account {} is not executable",
callee_program_id
);
return Err(InstructionError::AccountNotExecutable);
}
2021-04-26 10:06:40 -07:00
let programdata = if program_account.borrow().owner() == &bpf_loader_upgradeable::id() {
if let UpgradeableLoaderState::Program {
programdata_address,
} = program_account.borrow().state()?
{
if let Some(account) = invoke_context.get_account(&programdata_address) {
Some((programdata_address, account))
} else {
ic_msg!(
invoke_context,
"Unknown upgradeable programdata account {}",
programdata_address,
);
return Err(InstructionError::MissingAccount);
}
} else {
ic_msg!(
invoke_context,
"Upgradeable program account state not valid {}",
callee_program_id,
);
return Err(InstructionError::MissingAccount);
}
} else {
None
};
let mut executable_accounts = vec![(callee_program_id, program_account)];
if let Some(programdata) = programdata {
executable_accounts.push(programdata);
}
(
message,
executable_accounts,
accounts,
keyed_account_indices_reordered,
caller_write_privileges,
invoke_context.is_feature_active(&demote_sysvar_write_locks::id()),
)
};
2020-12-14 15:35:10 -08:00
#[allow(clippy::deref_addrof)]
2020-12-14 15:35:10 -08:00
MessageProcessor::process_cross_program_instruction(
&message,
&executable_accounts,
2020-12-14 15:35:10 -08:00
&accounts,
&caller_write_privileges,
*(&mut *(invoke_context.borrow_mut())),
2020-12-14 15:35:10 -08:00
)?;
// Copy results back to caller
{
let invoke_context = invoke_context.borrow();
let keyed_accounts = invoke_context.get_keyed_accounts()?;
for (src_keyed_account_index, (account, dst_keyed_account_index)) in accounts
.iter()
.zip(keyed_account_indices_reordered)
.enumerate()
{
let dst_keyed_account = &keyed_accounts[dst_keyed_account_index];
let src_keyed_account = account.borrow();
if message.is_writable(src_keyed_account_index, demote_sysvar_write_locks)
&& !src_keyed_account.executable()
{
if dst_keyed_account.data_len()? != src_keyed_account.data().len()
&& dst_keyed_account.data_len()? != 0
{
// Only support for `CreateAccount` at this time.
// Need a way to limit total realloc size across multiple CPI calls
ic_msg!(
invoke_context,
"Inner instructions do not support realloc, only SystemProgram::CreateAccount",
);
return Err(InstructionError::InvalidRealloc);
}
2021-04-28 14:30:06 -07:00
dst_keyed_account
.try_account_ref_mut()?
.set_lamports(src_keyed_account.lamports());
dst_keyed_account
.try_account_ref_mut()?
2021-04-26 10:06:40 -07:00
.set_owner(*src_keyed_account.owner());
dst_keyed_account
.try_account_ref_mut()?
.set_data(src_keyed_account.data().to_vec());
2020-12-14 15:35:10 -08:00
}
}
}
Ok(())
}
2020-04-28 14:33:56 -07:00
/// Process a cross-program instruction
/// This method calls the instruction's program entrypoint function
2020-04-28 14:33:56 -07:00
pub fn process_cross_program_instruction(
message: &Message,
executable_accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)],
2021-03-09 13:06:07 -08:00
accounts: &[Rc<RefCell<AccountSharedData>>],
caller_write_privileges: &[bool],
2020-04-28 14:33:56 -07:00
invoke_context: &mut dyn InvokeContext,
) -> Result<(), InstructionError> {
if let Some(instruction) = message.instructions.get(0) {
let program_id = instruction.program_id(&message.account_keys);
// Verify the calling program hasn't misbehaved
invoke_context.verify_and_update(
message,
instruction,
accounts,
Some(caller_write_privileges),
)?;
// Construct keyed accounts
let keyed_accounts = Self::create_keyed_accounts(
message,
instruction,
executable_accounts,
accounts,
invoke_context.is_feature_active(&demote_sysvar_write_locks::id()),
);
// Invoke callee
invoke_context.push(program_id, &keyed_accounts)?;
let mut message_processor = MessageProcessor::default();
for (program_id, process_instruction) in invoke_context.get_programs().iter() {
message_processor.add_program(*program_id, *process_instruction);
}
let mut result = message_processor.process_instruction(
program_id,
&instruction.data,
invoke_context,
);
if result.is_ok() {
// Verify the called program has not misbehaved
result = invoke_context.verify_and_update(message, instruction, accounts, None);
}
2020-04-28 14:33:56 -07:00
// Restore previous state
invoke_context.pop();
result
} else {
// This function is always called with a valid instruction, if that changes return an error
Err(InstructionError::GenericError)
2020-04-28 14:33:56 -07:00
}
}
/// 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,
2021-03-09 13:06:07 -08:00
accounts: &[Rc<RefCell<AccountSharedData>>],
) -> Vec<PreAccount> {
let mut pre_accounts = Vec::with_capacity(instruction.accounts.len());
{
let mut work = |_unique_index: usize, account_index: usize| {
2020-04-28 14:33:56 -07:00
let key = &message.account_keys[account_index];
let account = accounts[account_index].borrow();
2021-03-10 09:48:41 -08:00
pre_accounts.push(PreAccount::new(key, &account));
Ok(())
};
2020-03-12 09:08:39 -07:00
let _ = instruction.visit_each_account(&mut work);
2020-02-21 13:28:35 -08:00
}
pre_accounts
}
/// Verify there are no outstanding borrows
pub fn verify_account_references(
accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)],
) -> Result<(), InstructionError> {
2020-04-28 14:33:56 -07:00
for (_, account) in accounts.iter() {
account
.try_borrow_mut()
.map_err(|_| InstructionError::AccountBorrowOutstanding)?;
}
Ok(())
}
2020-02-21 13:28:35 -08:00
/// Verify the results of an instruction
pub fn verify(
2020-02-21 13:28:35 -08:00
message: &Message,
instruction: &CompiledInstruction,
pre_accounts: &[PreAccount],
executable_accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)],
2021-03-09 13:06:07 -08:00
accounts: &[Rc<RefCell<AccountSharedData>>],
rent: &Rent,
2021-03-03 15:07:45 -08:00
timings: &mut ExecuteDetailsTimings,
demote_sysvar_write_locks: bool,
) -> Result<(), InstructionError> {
2020-04-28 14:33:56 -07:00
// Verify all executable accounts have zero outstanding refs
Self::verify_account_references(executable_accounts)?;
// 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]
.try_borrow_mut()
.map_err(|_| InstructionError::AccountBorrowOutstanding)?;
}
let account = accounts[account_index].borrow();
pre_accounts[unique_index].verify(
&program_id,
message.is_writable(account_index, demote_sysvar_write_locks),
rent,
&account,
2021-03-03 15:07:45 -08:00
timings,
true,
)?;
2020-04-28 14:33:56 -07:00
pre_sum += u128::from(pre_accounts[unique_index].lamports());
2021-04-29 08:44:46 -07:00
post_sum += u128::from(account.lamports());
Ok(())
};
2020-03-12 09:08:39 -07:00
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(())
}
2020-04-28 14:33:56 -07:00
/// Verify the results of a cross-program instruction
fn verify_and_update(
message: &Message,
instruction: &CompiledInstruction,
pre_accounts: &mut [PreAccount],
2021-03-09 13:06:07 -08:00
accounts: &[Rc<RefCell<AccountSharedData>>],
2020-04-28 14:33:56 -07:00
program_id: &Pubkey,
rent: &Rent,
caller_write_privileges: Option<&[bool]>,
2021-03-03 15:07:45 -08:00
timings: &mut ExecuteDetailsTimings,
demote_sysvar_write_locks: bool,
2020-04-28 14:33:56 -07:00
) -> Result<(), InstructionError> {
// 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| {
if account_index < message.account_keys.len() && account_index < accounts.len() {
let key = &message.account_keys[account_index];
let account = &accounts[account_index];
let is_writable = if let Some(caller_write_privileges) = caller_write_privileges {
caller_write_privileges[account_index]
} else {
message.is_writable(account_index, demote_sysvar_write_locks)
};
// Find the matching PreAccount
for pre_account in pre_accounts.iter_mut() {
if key == pre_account.key() {
{
// Verify account has no outstanding references
let _ = account
.try_borrow_mut()
.map_err(|_| InstructionError::AccountBorrowOutstanding)?;
}
let account = account.borrow();
pre_account.verify(
&program_id,
is_writable,
&rent,
&account,
timings,
false,
)?;
pre_sum += u128::from(pre_account.lamports());
2021-04-29 08:44:46 -07:00
post_sum += u128::from(account.lamports());
if is_writable && !account.executable() {
pre_account.update(&account);
}
return Ok(());
}
2020-04-28 14:33:56 -07:00
}
}
Err(InstructionError::MissingAccount)
};
instruction.visit_each_account(&mut work)?;
work(0, instruction.program_id_index as usize)?;
2020-04-28 14:33:56 -07:00
// 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 accounts are committed back to the bank only if this function returns Ok(_).
#[allow(clippy::too_many_arguments)]
fn execute_instruction(
&self,
2019-03-29 09:05:06 -07:00
message: &Message,
instruction: &CompiledInstruction,
executable_accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)],
2021-03-09 13:06:07 -08:00
accounts: &[Rc<RefCell<AccountSharedData>>],
account_deps: &[(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>,
2020-10-28 13:16:13 -07:00
bpf_compute_budget: BpfComputeBudget,
2021-03-03 15:07:45 -08:00
timings: &mut ExecuteDetailsTimings,
demote_sysvar_write_locks: bool,
2021-04-12 16:04:57 -07:00
account_db: Arc<Accounts>,
ancestors: &Ancestors,
) -> Result<(), InstructionError> {
// Fixup the special instructions key if present
// before the account pre-values are taken care of
if feature_set.is_active(&instructions_sysvar_enabled::id()) {
for (i, key) in message.account_keys.iter().enumerate() {
if instructions::check_id(key) {
let mut mut_account_ref = accounts[i].borrow_mut();
instructions::store_current_index(
2021-03-10 13:28:03 -08:00
mut_account_ref.data_as_mut_slice(),
instruction_index as u16,
);
break;
}
}
}
let program_id = instruction.program_id(&message.account_keys);
2020-04-28 14:33:56 -07:00
let mut invoke_context = ThisInvokeContext::new(
program_id,
2020-04-28 14:33:56 -07:00
rent_collector.rent,
message,
instruction,
executable_accounts,
accounts,
account_deps,
2020-10-29 09:57:14 -07:00
&self.programs,
log_collector,
2020-10-28 13:16:13 -07:00
bpf_compute_budget,
executors,
instruction_recorder,
feature_set,
2021-04-12 16:04:57 -07:00
account_db,
ancestors,
2020-04-28 14:33:56 -07:00
);
self.process_instruction(program_id, &instruction.data, &mut invoke_context)?;
Self::verify(
2020-02-21 13:28:35 -08:00
message,
instruction,
2020-04-28 14:33:56 -07:00
&invoke_context.pre_accounts,
executable_accounts,
accounts,
&rent_collector.rent,
2021-03-03 15:07:45 -08:00
timings,
demote_sysvar_write_locks,
)?;
2021-03-03 15:07:45 -08:00
timings.accumulate(&invoke_context.timings);
Ok(())
}
2019-03-29 09:05:06 -07:00
/// 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::type_complexity)]
2019-03-29 09:05:06 -07:00
pub fn process_message(
&self,
2019-03-29 09:05:06 -07:00
message: &Message,
loaders: &[Vec<(Pubkey, Rc<RefCell<AccountSharedData>>)>],
2021-03-09 13:06:07 -08:00
accounts: &[Rc<RefCell<AccountSharedData>>],
account_deps: &[(Pubkey, Rc<RefCell<AccountSharedData>>)],
rent_collector: &RentCollector,
log_collector: Option<Rc<LogCollector>>,
executors: Rc<RefCell<Executors>>,
instruction_recorders: Option<&[InstructionRecorder]>,
feature_set: Arc<FeatureSet>,
2020-10-28 13:16:13 -07:00
bpf_compute_budget: BpfComputeBudget,
2021-03-03 15:07:45 -08:00
timings: &mut ExecuteDetailsTimings,
2021-04-12 16:04:57 -07:00
account_db: Arc<Accounts>,
ancestors: &Ancestors,
) -> Result<(), TransactionError> {
let demote_sysvar_write_locks = feature_set.is_active(&demote_sysvar_write_locks::id());
2019-03-29 09:05:06 -07:00
for (instruction_index, instruction) in message.instructions.iter().enumerate() {
let instruction_recorder = instruction_recorders
.as_ref()
.map(|recorders| recorders[instruction_index].clone());
self.execute_instruction(
message,
instruction,
&loaders[instruction_index],
accounts,
account_deps,
rent_collector,
log_collector.clone(),
executors.clone(),
instruction_recorder,
instruction_index,
feature_set.clone(),
2020-10-28 13:16:13 -07:00
bpf_compute_budget,
2021-03-03 15:07:45 -08:00
timings,
demote_sysvar_write_locks,
2021-04-12 16:04:57 -07:00
account_db.clone(),
ancestors,
)
.map_err(|err| TransactionError::InstructionError(instruction_index as u8, err))?;
2019-03-11 15:35:25 -07:00
}
Ok(())
}
2019-03-11 15:35:25 -07:00
}
#[cfg(test)]
mod tests {
use super::*;
2020-01-28 17:03:20 -08:00
use solana_sdk::{
account::Account,
2020-01-28 17:03:20 -08:00
instruction::{AccountMeta, Instruction, InstructionError},
message::Message,
native_loader::create_loadable_account_for_test,
2020-01-28 17:03:20 -08:00
};
2020-04-28 14:33:56 -07:00
#[test]
fn test_invoke_context() {
const MAX_DEPTH: usize = 10;
let mut invoke_stack = vec![];
2020-04-28 14:33:56 -07:00
let mut keys = vec![];
let mut accounts = vec![];
let mut metas = vec![];
2020-04-28 14:33:56 -07:00
for i in 0..MAX_DEPTH {
invoke_stack.push(solana_sdk::pubkey::new_rand());
keys.push(solana_sdk::pubkey::new_rand());
2021-03-09 13:06:07 -08:00
accounts.push(Rc::new(RefCell::new(AccountSharedData::new(
2020-04-28 14:33:56 -07:00
i as u64,
1,
&invoke_stack[i],
2020-04-28 14:33:56 -07:00
))));
metas.push(AccountMeta::new(keys[i], false));
2020-04-28 14:33:56 -07:00
}
for program_id in invoke_stack.iter() {
accounts.push(Rc::new(RefCell::new(AccountSharedData::new(
1,
1,
&solana_sdk::pubkey::Pubkey::default(),
))));
metas.push(AccountMeta::new(*program_id, false));
}
let message = Message::new(
&[Instruction::new_with_bytes(invoke_stack[0], &[0], metas)],
None,
);
2021-04-12 16:04:57 -07:00
let ancestors = Ancestors::default();
let mut invoke_context = ThisInvokeContext::new(
&invoke_stack[0],
Rent::default(),
&message,
&message.instructions[0],
2020-10-29 09:57:14 -07:00
&[],
&accounts,
&[],
&[],
None,
2020-10-28 13:16:13 -07:00
BpfComputeBudget::default(),
Rc::new(RefCell::new(Executors::default())),
None,
Arc::new(FeatureSet::all_enabled()),
2021-04-12 16:04:57 -07:00
Arc::new(Accounts::default()),
&ancestors,
);
2020-04-28 14:33:56 -07:00
// Check call depth increases and has a limit
let mut depth_reached = 1;
for program_id in invoke_stack.iter().skip(1) {
if Err(InstructionError::CallDepth) == invoke_context.push(program_id, &[]) {
2020-04-28 14:33:56 -07:00
break;
}
depth_reached += 1;
}
assert_ne!(depth_reached, 0);
assert!(depth_reached < MAX_DEPTH);
// Mock each invocation
for owned_index in (1..depth_reached).rev() {
let not_owned_index = owned_index - 1;
let metas = vec![
AccountMeta::new(keys[not_owned_index], false),
AccountMeta::new(keys[owned_index], false),
];
let message = Message::new(
2021-03-03 21:46:48 -08:00
&[Instruction::new_with_bytes(
invoke_stack[owned_index],
2021-03-03 21:46:48 -08:00
&[0],
metas,
)],
None,
);
2020-04-28 14:33:56 -07:00
// modify account owned by the program
2021-03-10 13:28:03 -08:00
accounts[owned_index].borrow_mut().data_as_mut_slice()[0] =
(MAX_DEPTH + owned_index) as u8;
2020-12-13 17:26:34 -08:00
let mut these_accounts = accounts[not_owned_index..owned_index + 1].to_vec();
2021-03-09 13:06:07 -08:00
these_accounts.push(Rc::new(RefCell::new(AccountSharedData::new(
1,
1,
&solana_sdk::pubkey::Pubkey::default(),
))));
2020-04-28 14:33:56 -07:00
invoke_context
.verify_and_update(&message, &message.instructions[0], &these_accounts, None)
2020-04-28 14:33:56 -07:00
.unwrap();
assert_eq!(
invoke_context.pre_accounts[owned_index]
.account
.borrow()
2021-03-09 14:31:33 -08:00
.data()[0],
2020-04-28 14:33:56 -07:00
(MAX_DEPTH + owned_index) as u8
);
// modify account not owned by the program
2021-03-09 14:31:33 -08:00
let data = accounts[not_owned_index].borrow_mut().data()[0];
2021-03-10 13:28:03 -08:00
accounts[not_owned_index].borrow_mut().data_as_mut_slice()[0] =
(MAX_DEPTH + not_owned_index) as u8;
2020-04-28 14:33:56 -07:00
assert_eq!(
invoke_context.verify_and_update(
&message,
&message.instructions[0],
&accounts[not_owned_index..owned_index + 1],
None
2020-04-28 14:33:56 -07:00
),
Err(InstructionError::ExternalAccountDataModified)
);
assert_eq!(
invoke_context.pre_accounts[not_owned_index]
.account
.borrow()
2021-03-09 14:31:33 -08:00
.data()[0],
data
);
2021-03-10 13:28:03 -08:00
accounts[not_owned_index].borrow_mut().data_as_mut_slice()[0] = data;
2020-04-28 14:33:56 -07:00
invoke_context.pop();
}
}
#[test]
fn test_is_zeroed() {
const ZEROS_LEN: usize = 1024;
let mut buf = [0; ZEROS_LEN];
assert_eq!(PreAccount::is_zeroed(&buf), true);
buf[0] = 1;
assert_eq!(PreAccount::is_zeroed(&buf), false);
let mut buf = [0; ZEROS_LEN - 1];
assert_eq!(PreAccount::is_zeroed(&buf), true);
buf[0] = 1;
assert_eq!(PreAccount::is_zeroed(&buf), false);
let mut buf = [0; ZEROS_LEN + 1];
assert_eq!(PreAccount::is_zeroed(&buf), true);
buf[0] = 1;
assert_eq!(PreAccount::is_zeroed(&buf), false);
let buf = vec![];
assert_eq!(PreAccount::is_zeroed(&buf), true);
}
#[test]
fn test_verify_account_references() {
2020-10-19 12:23:14 -07:00
let accounts = vec![(
solana_sdk::pubkey::new_rand(),
Rc::new(RefCell::new(AccountSharedData::default())),
2020-10-19 12:23:14 -07:00
)];
2020-04-28 14:33:56 -07:00
assert!(MessageProcessor::verify_account_references(&accounts).is_ok());
2020-04-28 14:33:56 -07:00
let mut _borrowed = accounts[0].1.borrow();
2020-01-22 17:54:06 -08:00
assert_eq!(
2020-04-28 14:33:56 -07:00
MessageProcessor::verify_account_references(&accounts),
2020-01-22 17:54:06 -08:00
Err(InstructionError::AccountBorrowOutstanding)
);
}
struct Change {
2020-04-18 17:04:13 -07:00
program_id: Pubkey,
is_writable: bool,
2020-04-28 14:33:56 -07:00
rent: Rent,
2020-04-18 17:04:13 -07:00
pre: PreAccount,
2021-03-09 13:06:07 -08:00
post: AccountSharedData,
2020-04-18 17:04:13 -07:00
}
impl Change {
2020-04-18 17:04:13 -07:00
pub fn new(owner: &Pubkey, program_id: &Pubkey) -> Self {
Self {
program_id: *program_id,
2020-04-28 14:33:56 -07:00
rent: Rent::default(),
is_writable: true,
2020-04-18 17:04:13 -07:00
pre: PreAccount::new(
&solana_sdk::pubkey::new_rand(),
&AccountSharedData::from(Account {
2020-04-18 17:04:13 -07:00
owner: *owner,
lamports: std::u64::MAX,
data: vec![],
..Account::default()
}),
2020-04-18 17:04:13 -07:00
),
post: AccountSharedData::from(Account {
2020-04-18 17:04:13 -07:00
owner: *owner,
lamports: std::u64::MAX,
..Account::default()
}),
2020-04-18 17:04:13 -07:00
}
}
pub fn read_only(mut self) -> Self {
self.is_writable = false;
2020-04-18 17:04:13 -07:00
self
}
pub fn executable(mut self, pre: bool, post: bool) -> Self {
self.pre.account.borrow_mut().set_executable(pre);
self.post.set_executable(post);
2020-04-18 17:04:13 -07:00
self
}
pub fn lamports(mut self, pre: u64, post: u64) -> Self {
self.pre.account.borrow_mut().set_lamports(pre);
self.post.set_lamports(post);
2020-04-18 17:04:13 -07:00
self
}
pub fn owner(mut self, post: &Pubkey) -> Self {
self.post.set_owner(*post);
2020-04-18 17:04:13 -07:00
self
}
pub fn data(mut self, pre: Vec<u8>, post: Vec<u8>) -> Self {
self.pre.account.borrow_mut().set_data(pre);
self.post.set_data(post);
2020-04-18 17:04:13 -07:00
self
2019-02-22 12:08:54 -08:00
}
2020-04-18 17:04:13 -07:00
pub fn rent_epoch(mut self, pre: u64, post: u64) -> Self {
self.pre.account.borrow_mut().set_rent_epoch(pre);
self.post.set_rent_epoch(post);
2020-04-18 17:04:13 -07:00
self
}
pub fn verify(&self) -> Result<(), InstructionError> {
self.pre.verify(
&self.program_id,
2021-03-10 09:48:41 -08:00
self.is_writable,
&self.rent,
&self.post,
2021-03-03 15:07:45 -08:00
&mut ExecuteDetailsTimings::default(),
false,
)
2020-04-28 14:33:56 -07:00
}
2020-04-18 17:04:13 -07:00
}
2019-02-22 12:08:54 -08:00
2020-04-18 17:04:13 -07:00
#[test]
fn test_verify_account_changes_owner() {
2019-02-22 12:08:54 -08:00
let system_program_id = system_program::id();
let alice_program_id = solana_sdk::pubkey::new_rand();
let mallory_program_id = solana_sdk::pubkey::new_rand();
2019-02-22 12:08:54 -08:00
assert_eq!(
2020-04-18 17:04:13 -07:00
Change::new(&system_program_id, &system_program_id)
.owner(&alice_program_id)
.verify(),
2019-02-22 12:08:54 -08:00
Ok(()),
"system program should be able to change the account owner"
);
assert_eq!(
2020-04-18 17:04:13 -07:00
Change::new(&system_program_id, &system_program_id)
.owner(&alice_program_id)
.read_only()
.verify(),
Err(InstructionError::ModifiedProgramId),
"system program should not be able to change the account owner of a read-only account"
);
assert_eq!(
2020-04-18 17:04:13 -07:00
Change::new(&mallory_program_id, &system_program_id)
.owner(&alice_program_id)
.verify(),
Err(InstructionError::ModifiedProgramId),
"system program should not be able to change the account owner of a non-system account"
);
assert_eq!(
2020-04-18 17:04:13 -07:00
Change::new(&mallory_program_id, &mallory_program_id)
.owner(&alice_program_id)
.verify(),
Ok(()),
"mallory should be able to change the account owner, if she leaves clear data"
);
assert_eq!(
2020-04-18 17:04:13 -07:00
Change::new(&mallory_program_id, &mallory_program_id)
.owner(&alice_program_id)
.data(vec![42], vec![0])
.verify(),
Ok(()),
"mallory should be able to change the account owner, if she leaves clear data"
);
assert_eq!(
Change::new(&mallory_program_id, &mallory_program_id)
.owner(&alice_program_id)
.executable(true, true)
.data(vec![42], vec![0])
.verify(),
Err(InstructionError::ModifiedProgramId),
"mallory should not be able to change the account owner, if the account executable"
);
assert_eq!(
2020-04-18 17:04:13 -07:00
Change::new(&mallory_program_id, &mallory_program_id)
.owner(&alice_program_id)
.data(vec![42], vec![42])
.verify(),
Err(InstructionError::ModifiedProgramId),
"mallory should not be able to inject data into the alice program"
2019-02-22 12:08:54 -08:00
);
}
#[test]
fn test_verify_account_changes_executable() {
let owner = solana_sdk::pubkey::new_rand();
let mallory_program_id = solana_sdk::pubkey::new_rand();
let system_program_id = system_program::id();
assert_eq!(
Change::new(&owner, &system_program_id)
.executable(false, true)
.verify(),
Err(InstructionError::ExecutableModified),
"system program can't change executable if system doesn't own the account"
);
assert_eq!(
Change::new(&owner, &system_program_id)
.executable(true, true)
.data(vec![1], vec![2])
.verify(),
Err(InstructionError::ExecutableDataModified),
"system program can't change executable data if system doesn't own the account"
);
assert_eq!(
Change::new(&owner, &owner).executable(false, true).verify(),
Ok(()),
"owner should be able to change executable"
);
assert_eq!(
Change::new(&owner, &owner)
.executable(false, true)
.read_only()
.verify(),
Err(InstructionError::ExecutableModified),
"owner can't modify executable of read-only accounts"
);
assert_eq!(
Change::new(&owner, &owner).executable(true, false).verify(),
Err(InstructionError::ExecutableModified),
"owner program can't reverse executable"
);
assert_eq!(
Change::new(&owner, &mallory_program_id)
.executable(false, true)
.verify(),
Err(InstructionError::ExecutableModified),
"malicious Mallory should not be able to change the account executable"
);
assert_eq!(
Change::new(&owner, &owner)
.executable(false, true)
.data(vec![1], vec![2])
.verify(),
Ok(()),
"account data can change in the same instruction that sets the bit"
);
assert_eq!(
Change::new(&owner, &owner)
.executable(true, true)
.data(vec![1], vec![2])
.verify(),
Err(InstructionError::ExecutableDataModified),
"owner should not be able to change an account's data once its marked executable"
);
assert_eq!(
Change::new(&owner, &owner)
.executable(true, true)
.lamports(1, 2)
.verify(),
Err(InstructionError::ExecutableLamportChange),
"owner should not be able to add lamports once marked executable"
);
assert_eq!(
Change::new(&owner, &owner)
.executable(true, true)
.lamports(1, 2)
.verify(),
Err(InstructionError::ExecutableLamportChange),
"owner should not be able to add lamports once marked executable"
);
assert_eq!(
Change::new(&owner, &owner)
.executable(true, true)
.lamports(2, 1)
.verify(),
Err(InstructionError::ExecutableLamportChange),
"owner should not be able to subtract lamports once marked executable"
);
let data = vec![1; 100];
2020-04-28 14:33:56 -07:00
let min_lamports = Rent::default().minimum_balance(data.len());
assert_eq!(
Change::new(&owner, &owner)
.executable(false, true)
.lamports(0, min_lamports)
.data(data.clone(), data.clone())
.verify(),
Ok(()),
);
assert_eq!(
Change::new(&owner, &owner)
.executable(false, true)
.lamports(0, min_lamports - 1)
.data(data.clone(), data)
.verify(),
Err(InstructionError::ExecutableAccountNotRentExempt),
"owner should not be able to change an account's data once its marked executable"
);
}
#[test]
fn test_verify_account_changes_data_len() {
let alice_program_id = solana_sdk::pubkey::new_rand();
2020-04-18 17:04:13 -07:00
assert_eq!(
2020-04-18 17:04:13 -07:00
Change::new(&system_program::id(), &system_program::id())
.data(vec![0], vec![0, 0])
.verify(),
Ok(()),
"system program should be able to change the data len"
);
assert_eq!(
2020-04-18 17:04:13 -07:00
Change::new(&alice_program_id, &system_program::id())
.data(vec![0], vec![0,0])
.verify(),
2020-04-28 14:33:56 -07:00
Err(InstructionError::AccountDataSizeChanged),
"system program should not be able to change the data length of accounts it does not own"
);
}
#[test]
fn test_verify_account_changes_data() {
let alice_program_id = solana_sdk::pubkey::new_rand();
let mallory_program_id = solana_sdk::pubkey::new_rand();
2019-02-22 12:08:54 -08:00
assert_eq!(
2020-04-18 17:04:13 -07:00
Change::new(&alice_program_id, &alice_program_id)
.data(vec![0], vec![42])
.verify(),
Ok(()),
"alice program should be able to change the data"
);
2019-02-22 12:08:54 -08:00
assert_eq!(
2020-04-18 17:04:13 -07:00
Change::new(&mallory_program_id, &alice_program_id)
.data(vec![0], vec![42])
.verify(),
Err(InstructionError::ExternalAccountDataModified),
2019-10-25 21:47:16 -07:00
"non-owner mallory should not be able to change the account data"
2019-02-22 12:08:54 -08:00
);
assert_eq!(
2020-04-18 17:04:13 -07:00
Change::new(&alice_program_id, &alice_program_id)
.data(vec![0], vec![42])
.read_only()
.verify(),
Err(InstructionError::ReadonlyDataModified),
2019-10-25 21:47:16 -07:00
"alice isn't allowed to touch a CO account"
);
}
#[test]
fn test_verify_account_changes_rent_epoch() {
let alice_program_id = solana_sdk::pubkey::new_rand();
assert_eq!(
2020-04-18 17:04:13 -07:00
Change::new(&alice_program_id, &system_program::id()).verify(),
Ok(()),
"nothing changed!"
);
assert_eq!(
2020-04-18 17:04:13 -07:00
Change::new(&alice_program_id, &system_program::id())
.rent_epoch(0, 1)
.verify(),
Err(InstructionError::RentEpochModified),
"no one touches rent_epoch"
);
}
#[test]
fn test_verify_account_changes_deduct_lamports_and_reassign_account() {
let alice_program_id = solana_sdk::pubkey::new_rand();
let bob_program_id = solana_sdk::pubkey::new_rand();
// positive test of this capability
assert_eq!(
2020-04-18 17:04:13 -07:00
Change::new(&alice_program_id, &alice_program_id)
.owner(&bob_program_id)
.lamports(42, 1)
.data(vec![42], vec![0])
.verify(),
2020-04-28 14:33:56 -07:00
Ok(()),
"alice should be able to deduct lamports and give the account to bob if the data is zeroed",
);
}
#[test]
fn test_verify_account_changes_lamports() {
let alice_program_id = solana_sdk::pubkey::new_rand();
assert_eq!(
2020-04-18 17:04:13 -07:00
Change::new(&alice_program_id, &system_program::id())
.lamports(42, 0)
.read_only()
.verify(),
Err(InstructionError::ExternalAccountLamportSpend),
"debit should fail, even if system program"
);
assert_eq!(
2020-04-18 17:04:13 -07:00
Change::new(&alice_program_id, &alice_program_id)
.lamports(42, 0)
.read_only()
.verify(),
Err(InstructionError::ReadonlyLamportChange),
"debit should fail, even if owning program"
);
assert_eq!(
2020-04-18 17:04:13 -07:00
Change::new(&alice_program_id, &system_program::id())
.lamports(42, 0)
.owner(&system_program::id())
.verify(),
Err(InstructionError::ModifiedProgramId),
"system program can't debit the account unless it was the pre.owner"
);
assert_eq!(
2020-04-18 17:04:13 -07:00
Change::new(&system_program::id(), &system_program::id())
.lamports(42, 0)
.owner(&alice_program_id)
.verify(),
Ok(()),
"system can spend (and change owner)"
);
}
#[test]
fn test_verify_account_changes_data_size_changed() {
let alice_program_id = solana_sdk::pubkey::new_rand();
2020-04-18 17:04:13 -07:00
assert_eq!(
2020-04-18 17:04:13 -07:00
Change::new(&alice_program_id, &system_program::id())
.data(vec![0], vec![0, 0])
.verify(),
Err(InstructionError::AccountDataSizeChanged),
"system program should not be able to change another program's account data size"
);
assert_eq!(
2020-04-18 17:04:13 -07:00
Change::new(&alice_program_id, &alice_program_id)
.data(vec![0], vec![0, 0])
.verify(),
Err(InstructionError::AccountDataSizeChanged),
"non-system programs cannot change their data size"
);
assert_eq!(
2020-04-18 17:04:13 -07:00
Change::new(&system_program::id(), &system_program::id())
.data(vec![0], vec![0, 0])
.verify(),
Ok(()),
"system program should be able to change account data size"
);
}
#[test]
fn test_process_message_readonly_handling() {
#[derive(Serialize, Deserialize)]
enum MockSystemInstruction {
Correct,
AttemptCredit { lamports: u64 },
AttemptDataChange { data: u8 },
}
fn mock_system_process_instruction(
_program_id: &Pubkey,
data: &[u8],
invoke_context: &mut dyn InvokeContext,
) -> Result<(), InstructionError> {
let keyed_accounts = invoke_context.get_keyed_accounts()?;
if let Ok(instruction) = bincode::deserialize(data) {
match instruction {
MockSystemInstruction::Correct => Ok(()),
MockSystemInstruction::AttemptCredit { lamports } => {
keyed_accounts[0].account.borrow_mut().lamports -= lamports;
2021-04-27 07:56:18 -07:00
keyed_accounts[1]
.account
.borrow_mut()
.checked_add_lamports(lamports)?;
Ok(())
}
// Change data in a read-only account
MockSystemInstruction::AttemptDataChange { data } => {
keyed_accounts[1].account.borrow_mut().set_data(vec![data]);
Ok(())
}
}
} else {
Err(InstructionError::InvalidInstructionData)
}
}
let mock_system_program_id = Pubkey::new(&[2u8; 32]);
let rent_collector = RentCollector::default();
let mut message_processor = MessageProcessor::default();
message_processor.add_program(mock_system_program_id, mock_system_process_instruction);
2021-03-09 13:06:07 -08:00
let mut accounts: Vec<Rc<RefCell<AccountSharedData>>> = Vec::new();
let account = AccountSharedData::new_ref(100, 1, &mock_system_program_id);
accounts.push(account);
2021-03-09 13:06:07 -08:00
let account = AccountSharedData::new_ref(0, 1, &mock_system_program_id);
accounts.push(account);
let mut loaders: Vec<Vec<(Pubkey, Rc<RefCell<AccountSharedData>>)>> = Vec::new();
let account = Rc::new(RefCell::new(create_loadable_account_for_test(
"mock_system_program",
)));
2019-09-24 04:09:53 -07:00
loaders.push(vec![(mock_system_program_id, account)]);
let executors = Rc::new(RefCell::new(Executors::default()));
2021-04-12 16:04:57 -07:00
let ancestors = Ancestors::default();
let from_pubkey = solana_sdk::pubkey::new_rand();
let to_pubkey = solana_sdk::pubkey::new_rand();
let account_metas = vec![
AccountMeta::new(from_pubkey, true),
AccountMeta::new_readonly(to_pubkey, false),
];
let message = Message::new(
2021-03-03 21:46:48 -08:00
&[Instruction::new_with_bincode(
mock_system_program_id,
&MockSystemInstruction::Correct,
account_metas.clone(),
)],
Some(&from_pubkey),
);
let result = message_processor.process_message(
&message,
&loaders,
&accounts,
&[],
&rent_collector,
None,
executors.clone(),
None,
Arc::new(FeatureSet::all_enabled()),
2021-02-18 09:56:11 -08:00
BpfComputeBudget::new(),
2021-03-03 15:07:45 -08:00
&mut ExecuteDetailsTimings::default(),
2021-04-12 16:04:57 -07:00
Arc::new(Accounts::default()),
&ancestors,
);
assert_eq!(result, Ok(()));
assert_eq!(accounts[0].borrow().lamports, 100);
assert_eq!(accounts[1].borrow().lamports, 0);
let message = Message::new(
2021-03-03 21:46:48 -08:00
&[Instruction::new_with_bincode(
mock_system_program_id,
&MockSystemInstruction::AttemptCredit { lamports: 50 },
account_metas.clone(),
)],
Some(&from_pubkey),
);
let result = message_processor.process_message(
&message,
&loaders,
&accounts,
&[],
&rent_collector,
None,
executors.clone(),
None,
Arc::new(FeatureSet::all_enabled()),
2021-02-18 09:56:11 -08:00
BpfComputeBudget::new(),
2021-03-03 15:07:45 -08:00
&mut ExecuteDetailsTimings::default(),
2021-04-12 16:04:57 -07:00
Arc::new(Accounts::default()),
&ancestors,
);
assert_eq!(
result,
Err(TransactionError::InstructionError(
0,
InstructionError::ReadonlyLamportChange
))
);
let message = Message::new(
2021-03-03 21:46:48 -08:00
&[Instruction::new_with_bincode(
mock_system_program_id,
&MockSystemInstruction::AttemptDataChange { data: 50 },
account_metas,
)],
Some(&from_pubkey),
);
let result = message_processor.process_message(
&message,
&loaders,
&accounts,
&[],
&rent_collector,
None,
executors,
None,
Arc::new(FeatureSet::all_enabled()),
2021-02-18 09:56:11 -08:00
BpfComputeBudget::new(),
2021-03-03 15:07:45 -08:00
&mut ExecuteDetailsTimings::default(),
2021-04-12 16:04:57 -07:00
Arc::new(Accounts::default()),
&ancestors,
);
assert_eq!(
result,
Err(TransactionError::InstructionError(
0,
InstructionError::ReadonlyDataModified
))
);
2019-02-22 12:08:54 -08:00
}
#[test]
fn test_process_message_duplicate_accounts() {
#[derive(Serialize, Deserialize)]
enum MockSystemInstruction {
BorrowFail,
MultiBorrowMut,
DoWork { lamports: u64, data: u8 },
}
fn mock_system_process_instruction(
_program_id: &Pubkey,
data: &[u8],
invoke_context: &mut dyn InvokeContext,
) -> Result<(), InstructionError> {
let keyed_accounts = invoke_context.get_keyed_accounts()?;
if let Ok(instruction) = bincode::deserialize(data) {
match instruction {
MockSystemInstruction::BorrowFail => {
let from_account = keyed_accounts[0].try_account_ref_mut()?;
let dup_account = keyed_accounts[2].try_account_ref_mut()?;
if from_account.lamports() != dup_account.lamports() {
return Err(InstructionError::InvalidArgument);
}
Ok(())
}
MockSystemInstruction::MultiBorrowMut => {
let from_lamports = {
let from_account = keyed_accounts[0].try_account_ref_mut()?;
from_account.lamports
};
let dup_lamports = {
let dup_account = keyed_accounts[2].try_account_ref_mut()?;
dup_account.lamports
};
if from_lamports != dup_lamports {
return Err(InstructionError::InvalidArgument);
}
Ok(())
}
MockSystemInstruction::DoWork { lamports, data } => {
{
let mut to_account = keyed_accounts[1].try_account_ref_mut()?;
let mut dup_account = keyed_accounts[2].try_account_ref_mut()?;
dup_account.lamports -= lamports;
2021-04-27 07:56:18 -07:00
to_account.checked_add_lamports(lamports)?;
dup_account.set_data(vec![data]);
}
keyed_accounts[0].try_account_ref_mut()?.lamports -= lamports;
2021-04-27 07:56:18 -07:00
keyed_accounts[1]
.try_account_ref_mut()?
.checked_add_lamports(lamports)?;
Ok(())
}
}
} else {
Err(InstructionError::InvalidInstructionData)
}
}
let mock_program_id = Pubkey::new(&[2u8; 32]);
let rent_collector = RentCollector::default();
let mut message_processor = MessageProcessor::default();
message_processor.add_program(mock_program_id, mock_system_process_instruction);
2021-03-09 13:06:07 -08:00
let mut accounts: Vec<Rc<RefCell<AccountSharedData>>> = Vec::new();
let account = AccountSharedData::new_ref(100, 1, &mock_program_id);
accounts.push(account);
2021-03-09 13:06:07 -08:00
let account = AccountSharedData::new_ref(0, 1, &mock_program_id);
accounts.push(account);
let mut loaders: Vec<Vec<(Pubkey, Rc<RefCell<AccountSharedData>>)>> = Vec::new();
let account = Rc::new(RefCell::new(create_loadable_account_for_test(
"mock_system_program",
)));
loaders.push(vec![(mock_program_id, account)]);
let executors = Rc::new(RefCell::new(Executors::default()));
2021-04-12 16:04:57 -07:00
let ancestors = Ancestors::default();
let from_pubkey = solana_sdk::pubkey::new_rand();
let to_pubkey = solana_sdk::pubkey::new_rand();
let dup_pubkey = from_pubkey;
let account_metas = vec![
AccountMeta::new(from_pubkey, true),
AccountMeta::new(to_pubkey, false),
AccountMeta::new(dup_pubkey, false),
];
// Try to borrow mut the same account
let message = Message::new(
2021-03-03 21:46:48 -08:00
&[Instruction::new_with_bincode(
mock_program_id,
&MockSystemInstruction::BorrowFail,
account_metas.clone(),
)],
Some(&from_pubkey),
);
let result = message_processor.process_message(
&message,
&loaders,
&accounts,
&[],
&rent_collector,
None,
executors.clone(),
None,
Arc::new(FeatureSet::all_enabled()),
2021-02-18 09:56:11 -08:00
BpfComputeBudget::new(),
2021-03-03 15:07:45 -08:00
&mut ExecuteDetailsTimings::default(),
2021-04-12 16:04:57 -07:00
Arc::new(Accounts::default()),
&ancestors,
);
assert_eq!(
result,
Err(TransactionError::InstructionError(
0,
InstructionError::AccountBorrowFailed
))
);
// Try to borrow mut the same account in a safe way
let message = Message::new(
2021-03-03 21:46:48 -08:00
&[Instruction::new_with_bincode(
mock_program_id,
&MockSystemInstruction::MultiBorrowMut,
account_metas.clone(),
)],
Some(&from_pubkey),
);
let result = message_processor.process_message(
&message,
&loaders,
&accounts,
&[],
&rent_collector,
None,
executors.clone(),
None,
Arc::new(FeatureSet::all_enabled()),
2021-02-18 09:56:11 -08:00
BpfComputeBudget::new(),
2021-03-03 15:07:45 -08:00
&mut ExecuteDetailsTimings::default(),
2021-04-12 16:04:57 -07:00
Arc::new(Accounts::default()),
&ancestors,
);
assert_eq!(result, Ok(()));
// Do work on the same account but at different location in keyed_accounts[]
let message = Message::new(
2021-03-03 21:46:48 -08:00
&[Instruction::new_with_bincode(
mock_program_id,
&MockSystemInstruction::DoWork {
lamports: 10,
data: 42,
},
account_metas,
)],
Some(&from_pubkey),
);
2021-04-12 16:04:57 -07:00
let ancestors = Ancestors::default();
let result = message_processor.process_message(
&message,
&loaders,
&accounts,
&[],
&rent_collector,
None,
executors,
None,
Arc::new(FeatureSet::all_enabled()),
2021-02-18 09:56:11 -08:00
BpfComputeBudget::new(),
2021-03-03 15:07:45 -08:00
&mut ExecuteDetailsTimings::default(),
2021-04-12 16:04:57 -07:00
Arc::new(Accounts::default()),
&ancestors,
);
assert_eq!(result, Ok(()));
assert_eq!(accounts[0].borrow().lamports, 80);
assert_eq!(accounts[1].borrow().lamports, 20);
2021-03-09 14:31:33 -08:00
assert_eq!(accounts[0].borrow().data(), &vec![42]);
}
2020-04-28 14:33:56 -07:00
#[test]
fn test_process_cross_program() {
#[derive(Debug, Serialize, Deserialize)]
2020-04-28 14:33:56 -07:00
enum MockInstruction {
NoopSuccess,
NoopFail,
ModifyOwned,
ModifyNotOwned,
}
fn mock_process_instruction(
program_id: &Pubkey,
data: &[u8],
invoke_context: &mut dyn InvokeContext,
2020-04-28 14:33:56 -07:00
) -> Result<(), InstructionError> {
let keyed_accounts = invoke_context.get_keyed_accounts()?;
2020-04-28 14:33:56 -07:00
assert_eq!(*program_id, keyed_accounts[0].owner()?);
assert_ne!(
keyed_accounts[1].owner()?,
2020-04-28 14:33:56 -07:00
*keyed_accounts[0].unsigned_key()
);
if let Ok(instruction) = bincode::deserialize(data) {
match instruction {
MockInstruction::NoopSuccess => (),
MockInstruction::NoopFail => return Err(InstructionError::GenericError),
MockInstruction::ModifyOwned => {
2021-03-10 13:28:03 -08:00
keyed_accounts[0].try_account_ref_mut()?.data_as_mut_slice()[0] = 1
2020-04-28 14:33:56 -07:00
}
MockInstruction::ModifyNotOwned => {
2021-03-10 13:28:03 -08:00
keyed_accounts[1].try_account_ref_mut()?.data_as_mut_slice()[0] = 1
2020-04-28 14:33:56 -07:00
}
}
} else {
return Err(InstructionError::InvalidInstructionData);
}
Ok(())
}
let caller_program_id = solana_sdk::pubkey::new_rand();
let callee_program_id = solana_sdk::pubkey::new_rand();
2021-03-09 13:06:07 -08:00
let mut program_account = AccountSharedData::new(1, 0, &native_loader::id());
program_account.set_executable(true);
let executable_accounts = vec![(
callee_program_id,
Rc::new(RefCell::new(program_account.clone())),
)];
2020-04-28 14:33:56 -07:00
let owned_key = solana_sdk::pubkey::new_rand();
2021-03-09 13:06:07 -08:00
let owned_account = AccountSharedData::new(42, 1, &callee_program_id);
2020-04-28 14:33:56 -07:00
let not_owned_key = solana_sdk::pubkey::new_rand();
2021-03-09 13:06:07 -08:00
let not_owned_account = AccountSharedData::new(84, 1, &solana_sdk::pubkey::new_rand());
2020-04-28 14:33:56 -07:00
#[allow(unused_mut)]
2020-04-28 14:33:56 -07:00
let mut accounts = vec![
Rc::new(RefCell::new(owned_account)),
Rc::new(RefCell::new(not_owned_account)),
Rc::new(RefCell::new(program_account)),
2020-04-28 14:33:56 -07:00
];
let compiled_instruction = CompiledInstruction::new(2, &(), vec![0, 1, 2]);
let programs: Vec<(_, ProcessInstructionWithContext)> =
vec![(callee_program_id, mock_process_instruction)];
let metas = vec![
AccountMeta::new(owned_key, false),
AccountMeta::new(not_owned_key, false),
];
let instruction = Instruction::new_with_bincode(
callee_program_id,
&MockInstruction::NoopSuccess,
metas.clone(),
);
let message = Message::new(&[instruction], None);
2021-04-12 16:04:57 -07:00
let ancestors = Ancestors::default();
2020-04-28 14:33:56 -07:00
let mut invoke_context = ThisInvokeContext::new(
&caller_program_id,
Rent::default(),
&message,
&compiled_instruction,
&executable_accounts,
&accounts,
&[],
programs.as_slice(),
None,
2020-10-28 13:16:13 -07:00
BpfComputeBudget::default(),
Rc::new(RefCell::new(Executors::default())),
None,
Arc::new(FeatureSet::all_enabled()),
2021-04-12 16:04:57 -07:00
Arc::new(Accounts::default()),
&ancestors,
2020-04-28 14:33:56 -07:00
);
// not owned account modified by the caller (before the invoke)
let demote_sysvar_write_locks =
invoke_context.is_feature_active(&demote_sysvar_write_locks::id());
let caller_write_privileges = message
.account_keys
.iter()
.enumerate()
.map(|(i, _)| message.is_writable(i, demote_sysvar_write_locks))
.collect::<Vec<bool>>();
accounts[0].borrow_mut().data_as_mut_slice()[0] = 1;
2020-04-28 14:33:56 -07:00
assert_eq!(
MessageProcessor::process_cross_program_instruction(
2020-04-28 14:33:56 -07:00
&message,
&executable_accounts,
&accounts,
&caller_write_privileges,
2020-04-28 14:33:56 -07:00
&mut invoke_context,
),
Err(InstructionError::ExternalAccountDataModified)
);
2021-03-10 13:28:03 -08:00
accounts[0].borrow_mut().data_as_mut_slice()[0] = 0;
2020-04-28 14:33:56 -07:00
let cases = vec![
(MockInstruction::NoopSuccess, Ok(())),
(
MockInstruction::NoopFail,
Err(InstructionError::GenericError),
),
(MockInstruction::ModifyOwned, Ok(())),
(
MockInstruction::ModifyNotOwned,
Err(InstructionError::ExternalAccountDataModified),
),
];
for case in cases {
2021-03-03 21:46:48 -08:00
let instruction =
Instruction::new_with_bincode(callee_program_id, &case.0, metas.clone());
let message = Message::new(&[instruction], None);
let ancestors = Ancestors::default();
let mut invoke_context = ThisInvokeContext::new(
&caller_program_id,
Rent::default(),
&message,
&compiled_instruction,
&executable_accounts,
&accounts,
&[],
programs.as_slice(),
None,
BpfComputeBudget::default(),
Rc::new(RefCell::new(Executors::default())),
None,
Arc::new(FeatureSet::all_enabled()),
Arc::new(Accounts::default()),
&ancestors,
);
let caller_write_privileges = message
.account_keys
.iter()
.enumerate()
.map(|(i, _)| message.is_writable(i, demote_sysvar_write_locks))
.collect::<Vec<bool>>();
2020-04-28 14:33:56 -07:00
assert_eq!(
MessageProcessor::process_cross_program_instruction(
2020-04-28 14:33:56 -07:00
&message,
&executable_accounts,
&accounts,
&caller_write_privileges,
2020-04-28 14:33:56 -07:00
&mut invoke_context,
),
case.1
);
}
}
#[test]
fn test_debug() {
let mut message_processor = MessageProcessor::default();
2020-12-13 17:26:34 -08:00
#[allow(clippy::unnecessary_wraps)]
fn mock_process_instruction(
_program_id: &Pubkey,
_data: &[u8],
_invoke_context: &mut dyn InvokeContext,
) -> Result<(), InstructionError> {
Ok(())
}
2020-12-13 17:26:34 -08:00
#[allow(clippy::unnecessary_wraps)]
fn mock_ix_processor(
_pubkey: &Pubkey,
_data: &[u8],
_context: &mut dyn InvokeContext,
2020-10-20 09:05:45 -07:00
) -> Result<(), InstructionError> {
Ok(())
}
let program_id = solana_sdk::pubkey::new_rand();
message_processor.add_program(program_id, mock_process_instruction);
message_processor.add_loader(program_id, mock_ix_processor);
assert!(!format!("{:?}", message_processor).is_empty());
}
}