2020-06-06 10:18:28 -07:00
|
|
|
use crate::{
|
2021-05-19 09:50:34 -07:00
|
|
|
accounts::Accounts, ancestors::Ancestors, instruction_recorder::InstructionRecorder,
|
2021-04-12 16:04:57 -07:00
|
|
|
log_collector::LogCollector, native_loader::NativeLoader, rent_collector::RentCollector,
|
2020-06-06 10:18:28 -07:00
|
|
|
};
|
|
|
|
use log::*;
|
2019-05-30 21:31:35 -07:00
|
|
|
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},
|
2020-12-17 15:39:49 -08:00
|
|
|
account_utils::StateMut,
|
|
|
|
bpf_loader_upgradeable::{self, UpgradeableLoaderState},
|
2021-03-30 10:05:09 -07:00
|
|
|
feature_set::{
|
|
|
|
cpi_share_ro_and_exec_accounts, demote_sysvar_write_locks, instructions_sysvar_enabled,
|
|
|
|
FeatureSet,
|
|
|
|
},
|
2021-01-28 10:04:54 -08:00
|
|
|
ic_msg,
|
2020-09-24 07:36:22 -07:00
|
|
|
instruction::{CompiledInstruction, Instruction, InstructionError},
|
2021-04-19 09:48:48 -07:00
|
|
|
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,
|
2020-10-28 20:21:50 -07:00
|
|
|
process_instruction::{
|
2021-04-19 09:48:48 -07:00
|
|
|
BpfComputeBudget, ComputeMeter, Executor, InvokeContext, InvokeContextStackFrame, Logger,
|
2020-10-28 13:16:13 -07:00
|
|
|
ProcessInstructionWithContext,
|
2020-10-28 20:21:50 -07:00
|
|
|
},
|
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,
|
2021-04-06 00:08:03 -07:00
|
|
|
sysvar::instructions,
|
2020-01-28 17:03:20 -08:00
|
|
|
transaction::TransactionError,
|
|
|
|
};
|
2021-01-28 10:04:54 -08:00
|
|
|
use std::{
|
|
|
|
cell::{Ref, RefCell},
|
|
|
|
collections::HashMap,
|
|
|
|
rc::Rc,
|
|
|
|
sync::Arc,
|
|
|
|
};
|
2020-09-14 17:42:37 -07:00
|
|
|
|
|
|
|
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()
|
|
|
|
}
|
|
|
|
}
|
2019-03-13 10:55:43 -07:00
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-05 10:57:32 -08:00
|
|
|
// 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)]
|
2020-02-21 11:30:00 -08:00
|
|
|
pub struct PreAccount {
|
2020-04-28 14:33:56 -07:00
|
|
|
key: Pubkey,
|
2021-03-10 23:04:00 -08:00
|
|
|
account: Rc<RefCell<AccountSharedData>>,
|
2021-03-03 15:07:45 -08:00
|
|
|
changed: bool,
|
2019-11-05 10:57:32 -08:00
|
|
|
}
|
2020-02-21 11:30:00 -08:00
|
|
|
impl PreAccount {
|
2021-03-10 09:48:41 -08:00
|
|
|
pub fn new(key: &Pubkey, account: &AccountSharedData) -> Self {
|
2019-11-05 10:57:32 -08:00
|
|
|
Self {
|
2020-04-28 14:33:56 -07:00
|
|
|
key: *key,
|
2021-03-10 23:04:00 -08:00
|
|
|
account: Rc::new(RefCell::new(account.clone())),
|
2021-03-03 15:07:45 -08:00
|
|
|
changed: false,
|
2019-11-05 10:57:32 -08:00
|
|
|
}
|
|
|
|
}
|
2019-10-24 11:06:00 -07:00
|
|
|
|
2020-03-31 10:07:38 -07:00
|
|
|
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,
|
2021-03-31 16:02:59 -07:00
|
|
|
outermost_call: bool,
|
2020-03-31 10:07:38 -07:00
|
|
|
) -> Result<(), InstructionError> {
|
2020-12-23 10:18:14 -08:00
|
|
|
let pre = self.account.borrow();
|
|
|
|
|
2020-02-21 11:30:00 -08:00
|
|
|
// Only the owner of the account may change owner and
|
|
|
|
// only if the account is writable and
|
2020-12-07 11:37:07 -08:00
|
|
|
// only if the account is not executable and
|
2020-02-21 11:30:00 -08:00
|
|
|
// 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
|
2021-01-22 15:28:01 -08:00
|
|
|
&& (!is_writable // line coverage used to get branch coverage
|
2021-04-27 07:12:17 -07:00
|
|
|
|| 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()))
|
2020-02-21 11:30:00 -08:00
|
|
|
{
|
|
|
|
return Err(InstructionError::ModifiedProgramId);
|
|
|
|
}
|
2019-10-24 11:06:00 -07:00
|
|
|
|
2020-02-21 11:30:00 -08:00
|
|
|
// 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()
|
2020-02-21 11:30:00 -08:00
|
|
|
{
|
|
|
|
return Err(InstructionError::ExternalAccountLamportSpend);
|
|
|
|
}
|
2019-10-24 11:06:00 -07:00
|
|
|
|
2020-03-27 16:43:25 -07:00
|
|
|
// 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 {
|
2021-01-22 15:28:01 -08:00
|
|
|
if !is_writable {
|
2020-03-27 16:43:25 -07:00
|
|
|
return Err(InstructionError::ReadonlyLamportChange);
|
|
|
|
}
|
2021-04-27 07:12:17 -07:00
|
|
|
if pre.executable() {
|
2020-03-27 16:43:25 -07:00
|
|
|
return Err(InstructionError::ExecutableLamportChange);
|
|
|
|
}
|
2020-02-21 11:30:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
2020-02-21 11:30:00 -08:00
|
|
|
&& (!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()))
|
2020-02-21 11:30:00 -08:00
|
|
|
{
|
|
|
|
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()
|
2021-01-22 15:28:01 -08:00
|
|
|
&& is_writable // line coverage used to get branch coverage
|
2021-04-27 07:12:17 -07:00
|
|
|
&& !pre.executable())
|
2021-03-09 14:31:33 -08:00
|
|
|
&& pre.data() != post.data()
|
2020-04-28 14:33:56 -07:00
|
|
|
{
|
2021-04-27 07:12:17 -07:00
|
|
|
if pre.executable() {
|
2020-04-28 14:33:56 -07:00
|
|
|
return Err(InstructionError::ExecutableDataModified);
|
2021-01-22 15:28:01 -08:00
|
|
|
} 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
|
|
|
}
|
2019-10-24 11:06:00 -07:00
|
|
|
}
|
|
|
|
|
2020-02-21 11:30:00 -08:00
|
|
|
// executable is one-way (false->true) and only the account owner may set it.
|
2021-04-27 07:12:17 -07:00
|
|
|
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()) {
|
2020-03-31 10:07:38 -07:00
|
|
|
return Err(InstructionError::ExecutableAccountNotRentExempt);
|
|
|
|
}
|
2021-01-22 15:28:01 -08:00
|
|
|
if !is_writable // line coverage used to get branch coverage
|
2021-04-27 07:12:17 -07:00
|
|
|
|| pre.executable()
|
2021-04-26 10:06:40 -07:00
|
|
|
|| program_id != pre.owner()
|
2020-03-31 10:07:38 -07:00
|
|
|
{
|
|
|
|
return Err(InstructionError::ExecutableModified);
|
|
|
|
}
|
2020-02-21 11:30:00 -08:00
|
|
|
}
|
|
|
|
|
2020-03-22 11:10:04 -07:00
|
|
|
// No one modifies rent_epoch (yet).
|
2021-04-28 06:52:20 -07:00
|
|
|
let rent_epoch_changed = pre.rent_epoch() != post.rent_epoch();
|
2021-03-03 15:07:45 -08:00
|
|
|
if rent_epoch_changed {
|
2020-02-21 11:30:00 -08:00
|
|
|
return Err(InstructionError::RentEpochModified);
|
|
|
|
}
|
2019-10-24 11:06:00 -07:00
|
|
|
|
2021-03-31 16:02:59 -07:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2020-02-21 11:30:00 -08:00
|
|
|
Ok(())
|
2019-08-26 11:04:20 -07:00
|
|
|
}
|
|
|
|
|
2021-03-09 13:06:07 -08:00
|
|
|
pub fn update(&mut self, account: &AccountSharedData) {
|
2020-12-23 10:18:14 -08:00
|
|
|
let mut pre = self.account.borrow_mut();
|
2021-03-31 07:11:39 -07:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2021-04-19 09:48:48 -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
|
|
|
}
|
|
|
|
|
2020-02-21 11:30:00 -08: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()]
|
|
|
|
}
|
2019-02-18 22:26:22 -08:00
|
|
|
}
|
|
|
|
|
2020-08-21 15:31:19 -07:00
|
|
|
pub struct ThisComputeMeter {
|
|
|
|
remaining: u64,
|
|
|
|
}
|
|
|
|
impl ComputeMeter for ThisComputeMeter {
|
|
|
|
fn consume(&mut self, amount: u64) -> Result<(), InstructionError> {
|
2020-09-25 11:08:10 -07:00
|
|
|
let exceeded = self.remaining < amount;
|
2020-08-21 15:31:19 -07:00
|
|
|
self.remaining = self.remaining.saturating_sub(amount);
|
2020-09-25 11:08:10 -07:00
|
|
|
if exceeded {
|
2020-08-21 15:31:19 -07:00
|
|
|
return Err(InstructionError::ComputationalBudgetExceeded);
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
fn get_remaining(&self) -> u64 {
|
|
|
|
self.remaining
|
|
|
|
}
|
|
|
|
}
|
2020-10-29 09:57:14 -07:00
|
|
|
pub struct ThisInvokeContext<'a> {
|
2021-04-19 09:48:48 -07:00
|
|
|
invoke_stack: Vec<InvokeContextStackFrame<'a>>,
|
2020-06-13 13:20:08 -07:00
|
|
|
rent: Rent,
|
2021-04-19 09:48:48 -07:00
|
|
|
message: &'a Message,
|
2020-06-13 13:20:08 -07:00
|
|
|
pre_accounts: Vec<PreAccount>,
|
2021-04-19 09:48:48 -07:00
|
|
|
executable_accounts: &'a [(Pubkey, Rc<RefCell<AccountSharedData>>)],
|
2021-03-10 23:04:00 -08:00
|
|
|
account_deps: &'a [(Pubkey, Rc<RefCell<AccountSharedData>>)],
|
2020-10-29 09:57:14 -07:00
|
|
|
programs: &'a [(Pubkey, ProcessInstructionWithContext)],
|
2020-06-13 13:20:08 -07:00
|
|
|
logger: Rc<RefCell<dyn Logger>>,
|
2020-10-28 13:16:13 -07:00
|
|
|
bpf_compute_budget: BpfComputeBudget,
|
2020-08-21 15:31:19 -07:00
|
|
|
compute_meter: Rc<RefCell<dyn ComputeMeter>>,
|
2020-09-14 17:42:37 -07:00
|
|
|
executors: Rc<RefCell<Executors>>,
|
2020-09-24 07:36:22 -07:00
|
|
|
instruction_recorder: Option<InstructionRecorder>,
|
2020-09-29 14:36:30 -07:00
|
|
|
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,
|
2021-05-20 14:00:50 -07:00
|
|
|
#[allow(clippy::type_complexity)]
|
|
|
|
sysvars: RefCell<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> {
|
2020-12-22 09:26:55 -08:00
|
|
|
#[allow(clippy::too_many_arguments)]
|
2020-06-03 12:48:19 -07:00
|
|
|
pub fn new(
|
|
|
|
program_id: &Pubkey,
|
|
|
|
rent: Rent,
|
2021-04-19 09:48:48 -07:00
|
|
|
message: &'a Message,
|
|
|
|
instruction: &'a CompiledInstruction,
|
|
|
|
executable_accounts: &'a [(Pubkey, Rc<RefCell<AccountSharedData>>)],
|
|
|
|
accounts: &'a [Rc<RefCell<AccountSharedData>>],
|
2021-03-10 23:04:00 -08:00
|
|
|
account_deps: &'a [(Pubkey, Rc<RefCell<AccountSharedData>>)],
|
2020-10-29 09:57:14 -07:00
|
|
|
programs: &'a [(Pubkey, ProcessInstructionWithContext)],
|
2020-06-13 13:20:08 -07:00
|
|
|
log_collector: Option<Rc<LogCollector>>,
|
2020-10-28 13:16:13 -07:00
|
|
|
bpf_compute_budget: BpfComputeBudget,
|
2020-09-14 17:42:37 -07:00
|
|
|
executors: Rc<RefCell<Executors>>,
|
2020-09-24 07:36:22 -07:00
|
|
|
instruction_recorder: Option<InstructionRecorder>,
|
2020-09-29 14:36:30 -07:00
|
|
|
feature_set: Arc<FeatureSet>,
|
2021-04-12 16:04:57 -07:00
|
|
|
account_db: Arc<Accounts>,
|
|
|
|
ancestors: &'a Ancestors,
|
2020-06-03 12:48:19 -07:00
|
|
|
) -> Self {
|
2021-04-19 09:48:48 -07:00
|
|
|
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,
|
2021-04-19 09:48:48 -07:00
|
|
|
message,
|
2020-04-28 14:33:56 -07:00
|
|
|
pre_accounts,
|
2021-04-19 09:48:48 -07:00
|
|
|
executable_accounts,
|
2020-12-22 09:26:55 -08:00
|
|
|
account_deps,
|
2020-06-03 12:48:19 -07:00
|
|
|
programs,
|
2020-06-13 13:20:08 -07:00
|
|
|
logger: Rc::new(RefCell::new(ThisLogger { log_collector })),
|
2020-10-28 13:16:13 -07:00
|
|
|
bpf_compute_budget,
|
2020-08-21 15:31:19 -07:00
|
|
|
compute_meter: Rc::new(RefCell::new(ThisComputeMeter {
|
2020-10-28 13:16:13 -07:00
|
|
|
remaining: bpf_compute_budget.max_units,
|
2020-08-21 15:31:19 -07:00
|
|
|
})),
|
2020-09-14 17:42:37 -07:00
|
|
|
executors,
|
2020-09-24 07:36:22 -07:00
|
|
|
instruction_recorder,
|
2020-09-29 14:36:30 -07:00
|
|
|
feature_set,
|
2021-03-03 15:07:45 -08:00
|
|
|
timings: ExecuteDetailsTimings::default(),
|
2021-04-12 16:04:57 -07:00
|
|
|
account_db,
|
|
|
|
ancestors,
|
2021-05-20 14:00:50 -07:00
|
|
|
sysvars: RefCell::new(vec![]),
|
2021-04-19 09:48:48 -07:00
|
|
|
};
|
|
|
|
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> {
|
2021-04-19 09:48:48 -07:00
|
|
|
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);
|
|
|
|
}
|
2021-04-19 09:48:48 -07:00
|
|
|
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);
|
|
|
|
}
|
2021-04-19 09:48:48 -07:00
|
|
|
// 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) {
|
2021-04-19 09:48:48 -07:00
|
|
|
self.invoke_stack.pop();
|
2020-04-28 14:33:56 -07:00
|
|
|
}
|
2020-11-12 12:44:37 -08:00
|
|
|
fn invoke_depth(&self) -> usize {
|
2021-04-19 09:48:48 -07:00
|
|
|
self.invoke_stack.len()
|
2020-11-12 12:44:37 -08:00
|
|
|
}
|
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>>],
|
2021-03-10 23:04:00 -08:00
|
|
|
caller_write_privileges: Option<&[bool]>,
|
2020-04-28 14:33:56 -07:00
|
|
|
) -> Result<(), InstructionError> {
|
2021-04-19 09:48:48 -07:00
|
|
|
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> {
|
2021-04-19 09:48:48 -07:00
|
|
|
self.invoke_stack
|
2020-05-08 12:24:36 -07:00
|
|
|
.last()
|
2021-04-19 09:48:48 -07:00
|
|
|
.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
|
|
|
}
|
2020-10-28 20:21:50 -07:00
|
|
|
fn get_programs(&self) -> &[(Pubkey, ProcessInstructionWithContext)] {
|
2020-10-29 09:57:14 -07:00
|
|
|
self.programs
|
2020-06-03 12:48:19 -07:00
|
|
|
}
|
2020-06-13 13:20:08 -07:00
|
|
|
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
|
2020-08-26 14:48:51 -07:00
|
|
|
}
|
2020-08-21 15:31:19 -07:00
|
|
|
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>) {
|
2020-09-14 17:42:37 -07:00
|
|
|
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>> {
|
2020-09-14 17:42:37 -07:00
|
|
|
self.executors.borrow().get(&pubkey)
|
|
|
|
}
|
2020-09-24 07:36:22 -07:00
|
|
|
fn record_instruction(&self, instruction: &Instruction) {
|
|
|
|
if let Some(recorder) = &self.instruction_recorder {
|
|
|
|
recorder.record_instruction(instruction.clone());
|
|
|
|
}
|
|
|
|
}
|
2020-09-29 14:36:30 -07:00
|
|
|
fn is_feature_active(&self, feature_id: &Pubkey) -> bool {
|
|
|
|
self.feature_set.is_active(feature_id)
|
|
|
|
}
|
2021-03-10 23:04:00 -08:00
|
|
|
fn get_account(&self, pubkey: &Pubkey) -> Option<Rc<RefCell<AccountSharedData>>> {
|
|
|
|
if self.is_feature_active(&cpi_share_ro_and_exec_accounts::id()) {
|
2021-04-19 09:48:48 -07:00
|
|
|
if let Some((_, account)) = self
|
|
|
|
.executable_accounts
|
|
|
|
.iter()
|
|
|
|
.find(|(key, _)| key == pubkey)
|
|
|
|
{
|
2021-03-10 23:04:00 -08:00
|
|
|
Some(account.clone())
|
|
|
|
} else if let Some((_, account)) =
|
|
|
|
self.account_deps.iter().find(|(key, _)| key == pubkey)
|
|
|
|
{
|
|
|
|
Some(account.clone())
|
2020-12-22 09:26:55 -08:00
|
|
|
} else {
|
2021-04-08 11:40:37 -07:00
|
|
|
self.pre_accounts
|
|
|
|
.iter()
|
|
|
|
.find(|pre| pre.key == *pubkey)
|
|
|
|
.map(|pre| pre.account.clone())
|
2020-12-22 09:26:55 -08:00
|
|
|
}
|
2021-03-10 23:04:00 -08:00
|
|
|
} 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);
|
2020-12-17 15:39:49 -08:00
|
|
|
}
|
2021-03-10 23:04:00 -08:00
|
|
|
self.account_deps.iter().find_map(|(key, account)| {
|
|
|
|
if key == pubkey {
|
|
|
|
Some(account.clone())
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
2020-12-17 15:39:49 -08:00
|
|
|
}
|
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-05-20 14:00:50 -07:00
|
|
|
fn get_sysvar_data(&self, id: &Pubkey) -> Option<Rc<Vec<u8>>> {
|
|
|
|
if let Ok(mut sysvars) = self.sysvars.try_borrow_mut() {
|
|
|
|
// Try share from cache
|
|
|
|
let mut result = sysvars
|
2021-04-12 16:04:57 -07:00
|
|
|
.iter()
|
|
|
|
.find_map(|(key, sysvar)| if id == key { sysvar.clone() } else { None });
|
2021-05-20 14:00:50 -07:00
|
|
|
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()));
|
|
|
|
// Cache it
|
|
|
|
sysvars.push((*id, result.clone()));
|
|
|
|
}
|
|
|
|
result
|
|
|
|
} else {
|
|
|
|
None
|
2021-04-12 16:04:57 -07:00
|
|
|
}
|
|
|
|
}
|
2020-06-13 13:20:08 -07:00
|
|
|
}
|
|
|
|
pub struct ThisLogger {
|
|
|
|
log_collector: Option<Rc<LogCollector>>,
|
|
|
|
}
|
|
|
|
impl Logger for ThisLogger {
|
2020-06-06 10:18:28 -07:00
|
|
|
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) {
|
2020-11-12 12:44:37 -08:00
|
|
|
debug!("{}", message);
|
2020-06-13 13:20:08 -07:00
|
|
|
if let Some(log_collector) = &self.log_collector {
|
2020-06-06 10:18:28 -07:00
|
|
|
log_collector.log(message);
|
|
|
|
}
|
|
|
|
}
|
2020-04-28 14:33:56 -07:00
|
|
|
}
|
|
|
|
|
2020-07-29 15:29:52 -07:00
|
|
|
#[derive(Deserialize, Serialize)]
|
2019-04-02 08:35:38 -07:00
|
|
|
pub struct MessageProcessor {
|
2019-05-30 21:31:35 -07:00
|
|
|
#[serde(skip)]
|
2020-10-28 20:21:50 -07:00
|
|
|
programs: Vec<(Pubkey, ProcessInstructionWithContext)>,
|
2019-05-30 21:31:35 -07:00
|
|
|
#[serde(skip)]
|
2020-04-15 09:41:29 -07:00
|
|
|
native_loader: NativeLoader,
|
2020-07-29 15:29:52 -07:00
|
|
|
}
|
2020-08-25 09:49:15 -07:00
|
|
|
|
|
|
|
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>;
|
|
|
|
|
2020-08-25 09:49:15 -07:00
|
|
|
// 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)| {
|
2020-10-28 20:21:50 -07:00
|
|
|
let erased_instruction: ErasedProcessInstructionWithContext = *instruction;
|
2020-08-25 09:49:15 -07:00
|
|
|
format!("{}: {:p}", pubkey, erased_instruction)
|
|
|
|
})
|
|
|
|
.collect::<Vec<_>>(),
|
|
|
|
native_loader: &self.native_loader,
|
|
|
|
};
|
|
|
|
|
|
|
|
write!(f, "{:?}", processor)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-29 15:29:52 -07:00
|
|
|
impl Default for MessageProcessor {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
programs: vec![],
|
|
|
|
native_loader: NativeLoader::default(),
|
|
|
|
}
|
|
|
|
}
|
2019-03-15 19:48:11 -07:00
|
|
|
}
|
2020-04-27 21:05:12 -07:00
|
|
|
impl Clone for MessageProcessor {
|
|
|
|
fn clone(&self) -> Self {
|
|
|
|
MessageProcessor {
|
2020-05-19 19:45:30 -07:00
|
|
|
programs: self.programs.clone(),
|
2020-04-15 09:41:29 -07:00
|
|
|
native_loader: NativeLoader::default(),
|
2019-03-16 16:20:09 -07:00
|
|
|
}
|
2019-03-15 19:48:11 -07:00
|
|
|
}
|
|
|
|
}
|
2020-07-06 04:22:23 -07:00
|
|
|
|
|
|
|
#[cfg(RUSTC_WITH_SPECIALIZATION)]
|
2020-10-19 21:07:46 -07:00
|
|
|
impl ::solana_frozen_abi::abi_example::AbiExample for MessageProcessor {
|
2020-07-06 04:22:23 -07:00
|
|
|
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 {
|
2019-07-31 14:28:14 -07:00
|
|
|
/// Add a static entrypoint to intercept instructions before the dynamic loader.
|
2020-10-28 20:21:50 -07:00
|
|
|
pub fn add_program(
|
|
|
|
&mut self,
|
|
|
|
program_id: Pubkey,
|
|
|
|
process_instruction: ProcessInstructionWithContext,
|
|
|
|
) {
|
2020-05-19 19:45:30 -07:00
|
|
|
match self.programs.iter_mut().find(|(key, _)| program_id == *key) {
|
2020-04-27 21:05:12 -07:00
|
|
|
Some((_, processor)) => *processor = process_instruction,
|
2020-05-19 19:45:30 -07:00
|
|
|
None => self.programs.push((program_id, process_instruction)),
|
2020-04-27 21:05:12 -07:00
|
|
|
}
|
2019-03-07 12:42:01 -08:00
|
|
|
}
|
2019-03-15 19:48:11 -07:00
|
|
|
|
2020-05-20 09:24:57 -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-05-20 09:24:57 -07:00
|
|
|
}
|
|
|
|
|
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,
|
2021-03-10 23:04:00 -08:00
|
|
|
executable_accounts: &'a [(Pubkey, Rc<RefCell<AccountSharedData>>)],
|
2021-03-09 13:06:07 -08:00
|
|
|
accounts: &'a [Rc<RefCell<AccountSharedData>>],
|
2021-03-30 10:05:09 -07:00
|
|
|
demote_sysvar_write_locks: bool,
|
2021-04-19 09:48:48 -07:00
|
|
|
) -> Vec<(bool, bool, &'a Pubkey, &'a RefCell<AccountSharedData>)> {
|
|
|
|
executable_accounts
|
2019-03-07 09:35:28 -08:00
|
|
|
.iter()
|
2021-04-19 09:48:48 -07:00
|
|
|
.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,
|
2020-12-23 19:04:48 -08:00
|
|
|
program_id: &Pubkey,
|
2020-05-20 09:24:57 -07:00
|
|
|
instruction_data: &[u8],
|
2020-04-28 14:33:56 -07:00
|
|
|
invoke_context: &mut dyn InvokeContext,
|
|
|
|
) -> Result<(), InstructionError> {
|
2021-04-19 09:48:48 -07:00
|
|
|
if let Some(root_account) = invoke_context.get_keyed_accounts()?.iter().next() {
|
2020-12-23 19:04:48 -08:00
|
|
|
let root_id = root_account.unsigned_key();
|
2020-10-12 13:40:04 -07:00
|
|
|
if native_loader::check_id(&root_account.owner()?) {
|
|
|
|
for (id, process_instruction) in &self.programs {
|
|
|
|
if id == root_id {
|
2021-04-19 09:48:48 -07:00
|
|
|
invoke_context.remove_first_keyed_account()?;
|
2020-10-12 13:40:04 -07:00
|
|
|
// Call the builtin program
|
2021-04-19 09:48:48 -07:00
|
|
|
return process_instruction(&program_id, instruction_data, invoke_context);
|
2020-10-12 13:40:04 -07:00
|
|
|
}
|
2020-05-20 09:24:57 -07:00
|
|
|
}
|
2020-10-12 13:40:04 -07:00
|
|
|
// Call the program via the native loader
|
|
|
|
return self.native_loader.process_instruction(
|
2021-01-11 14:36:52 -08:00
|
|
|
&native_loader::id(),
|
2020-10-12 13:40:04 -07:00
|
|
|
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 {
|
2020-10-12 13:40:04 -07:00
|
|
|
if id == owner_id {
|
|
|
|
// Call the program via a builtin loader
|
2021-04-19 09:48:48 -07:00
|
|
|
return process_instruction(&program_id, instruction_data, invoke_context);
|
2020-10-12 13:40:04 -07:00
|
|
|
}
|
2020-05-20 09:24:57 -07:00
|
|
|
}
|
|
|
|
}
|
2020-04-15 09:41:29 -07:00
|
|
|
}
|
2020-05-20 09:24:57 -07:00
|
|
|
Err(InstructionError::UnsupportedProgramId)
|
2019-03-15 19:48:11 -07:00
|
|
|
}
|
|
|
|
|
2020-12-10 18:25:57 -08:00
|
|
|
pub fn create_message(
|
|
|
|
instruction: &Instruction,
|
|
|
|
keyed_accounts: &[&KeyedAccount],
|
|
|
|
signers: &[Pubkey],
|
2021-01-28 10:04:54 -08:00
|
|
|
invoke_context: &Ref<&mut dyn InvokeContext>,
|
2020-12-23 14:34:14 -08:00
|
|
|
) -> Result<(Message, Pubkey, usize), InstructionError> {
|
2020-12-10 18:25:57 -08:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
})
|
2021-01-28 10:04:54 -08:00
|
|
|
.ok_or_else(|| {
|
|
|
|
ic_msg!(
|
|
|
|
invoke_context,
|
2021-02-01 11:40:49 -08:00
|
|
|
"Instruction references an unknown account {}",
|
2021-01-28 10:04:54 -08:00
|
|
|
account.pubkey
|
|
|
|
);
|
|
|
|
InstructionError::MissingAccount
|
|
|
|
})?;
|
2020-12-10 18:25:57 -08:00
|
|
|
// Readonly account cannot become writable
|
|
|
|
if account.is_writable && !keyed_account.is_writable() {
|
2021-01-28 10:04:54 -08:00
|
|
|
ic_msg!(
|
|
|
|
invoke_context,
|
2021-02-01 11:40:49 -08:00
|
|
|
"{}'s writable privilege escalated",
|
2021-01-28 10:04:54 -08:00
|
|
|
account.pubkey
|
|
|
|
);
|
2020-12-10 18:25:57 -08:00
|
|
|
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
|
|
|
|
) {
|
2021-01-28 10:04:54 -08:00
|
|
|
ic_msg!(
|
|
|
|
invoke_context,
|
2021-03-04 23:16:53 -08:00
|
|
|
"{}'s signer privilege escalated",
|
2021-01-28 10:04:54 -08:00
|
|
|
account.pubkey
|
|
|
|
);
|
2020-12-10 18:25:57 -08:00
|
|
|
return Err(InstructionError::PrivilegeEscalation);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-14 00:19:22 -08:00
|
|
|
// validate the caller has access to the program account and that it is executable
|
2020-12-23 14:34:14 -08:00
|
|
|
let program_id = instruction.program_id;
|
2021-01-14 00:19:22 -08:00
|
|
|
match keyed_accounts
|
2020-12-10 18:25:57 -08:00
|
|
|
.iter()
|
2021-01-14 00:19:22 -08:00
|
|
|
.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()
|
|
|
|
);
|
2021-01-14 00:19:22 -08:00
|
|
|
return Err(InstructionError::AccountNotExecutable);
|
2020-12-10 18:25:57 -08:00
|
|
|
}
|
2021-01-14 00:19:22 -08:00
|
|
|
}
|
2021-01-28 10:04:54 -08:00
|
|
|
None => {
|
2021-02-01 11:40:49 -08:00
|
|
|
ic_msg!(invoke_context, "Unknown program {}", program_id);
|
2021-01-28 10:04:54 -08:00
|
|
|
return Err(InstructionError::MissingAccount);
|
|
|
|
}
|
2021-01-14 00:19:22 -08:00
|
|
|
}
|
2020-12-10 18:25:57 -08:00
|
|
|
|
|
|
|
let message = Message::new(&[instruction.clone()], None);
|
2020-12-23 14:34:14 -08:00
|
|
|
let program_id_index = message.instructions[0].program_id_index as usize;
|
|
|
|
|
|
|
|
Ok((message, program_id, program_id_index))
|
2020-12-10 18:25:57 -08:00
|
|
|
}
|
|
|
|
|
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,
|
2021-04-19 09:48:48 -07:00
|
|
|
keyed_account_indices: &[usize],
|
|
|
|
signers: &[Pubkey],
|
2020-12-14 15:35:10 -08:00
|
|
|
) -> Result<(), InstructionError> {
|
2021-01-28 10:04:54 -08:00
|
|
|
let invoke_context = RefCell::new(invoke_context);
|
2020-12-14 15:35:10 -08:00
|
|
|
|
2021-03-30 10:05:09 -07:00
|
|
|
let (
|
|
|
|
message,
|
2021-04-19 09:48:48 -07:00
|
|
|
executable_accounts,
|
2021-03-30 10:05:09 -07:00
|
|
|
accounts,
|
2021-04-19 09:48:48 -07:00
|
|
|
keyed_account_indices_reordered,
|
2021-03-30 10:05:09 -07:00
|
|
|
caller_write_privileges,
|
|
|
|
demote_sysvar_write_locks,
|
|
|
|
) = {
|
2021-01-28 10:04:54 -08:00
|
|
|
let invoke_context = invoke_context.borrow();
|
2020-12-14 15:35:10 -08:00
|
|
|
|
2021-01-28 10:04:54 -08:00
|
|
|
// Translate and verify caller's data
|
2021-04-19 09:48:48 -07:00
|
|
|
let keyed_accounts = invoke_context.get_keyed_accounts()?;
|
|
|
|
let keyed_accounts = keyed_account_indices
|
2021-01-28 10:04:54 -08:00
|
|
|
.iter()
|
2021-04-19 09:48:48 -07:00
|
|
|
.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
|
2021-01-28 10:04:54 -08:00
|
|
|
.iter()
|
2021-04-19 09:48:48 -07:00
|
|
|
.map(|index| keyed_accounts[*index].is_writable())
|
2021-01-28 10:04:54 -08:00
|
|
|
.collect::<Vec<bool>>();
|
2021-03-10 23:04:00 -08:00
|
|
|
caller_write_privileges.insert(0, false);
|
2021-01-28 10:04:54 -08:00
|
|
|
let mut accounts = vec![];
|
2021-04-19 09:48:48 -07:00
|
|
|
let mut keyed_account_indices_reordered = vec![];
|
|
|
|
let keyed_accounts = invoke_context.get_keyed_accounts()?;
|
2021-01-28 10:04:54 -08:00
|
|
|
'root: for account_key in message.account_keys.iter() {
|
2021-04-19 09:48:48 -07:00
|
|
|
for keyed_account_index in keyed_account_indices {
|
|
|
|
let keyed_account = &keyed_accounts[*keyed_account_index];
|
2021-01-28 10:04:54 -08:00
|
|
|
if account_key == keyed_account.unsigned_key() {
|
|
|
|
accounts.push(Rc::new(keyed_account.account.clone()));
|
2021-04-19 09:48:48 -07:00
|
|
|
keyed_account_indices_reordered.push(*keyed_account_index);
|
2021-01-28 10:04:54 -08:00
|
|
|
continue 'root;
|
|
|
|
}
|
2020-12-14 15:35:10 -08:00
|
|
|
}
|
2021-01-28 10:04:54 -08:00
|
|
|
ic_msg!(
|
|
|
|
invoke_context,
|
2021-02-01 11:40:49 -08:00
|
|
|
"Instruction references an unknown account {}",
|
2021-01-28 10:04:54 -08:00
|
|
|
account_key
|
|
|
|
);
|
|
|
|
return Err(InstructionError::MissingAccount);
|
2020-12-14 15:35:10 -08:00
|
|
|
}
|
|
|
|
|
2021-01-28 10:04:54 -08:00
|
|
|
// Process instruction
|
2020-12-14 15:35:10 -08:00
|
|
|
|
2021-01-28 10:04:54 -08:00
|
|
|
invoke_context.record_instruction(&instruction);
|
2020-12-17 15:39:49 -08:00
|
|
|
|
2021-01-28 10:04:54 -08:00
|
|
|
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);
|
2021-01-28 10:04:54 -08:00
|
|
|
InstructionError::MissingAccount
|
|
|
|
})?;
|
2021-04-27 07:12:17 -07:00
|
|
|
if !program_account.borrow().executable() {
|
2021-02-01 11:40:49 -08:00
|
|
|
ic_msg!(
|
|
|
|
invoke_context,
|
|
|
|
"Account {} is not executable",
|
|
|
|
callee_program_id
|
|
|
|
);
|
2021-01-28 10:04:54 -08:00
|
|
|
return Err(InstructionError::AccountNotExecutable);
|
|
|
|
}
|
2021-04-26 10:06:40 -07:00
|
|
|
let programdata = if program_account.borrow().owner() == &bpf_loader_upgradeable::id() {
|
2021-04-19 09:48:48 -07:00
|
|
|
if let UpgradeableLoaderState::Program {
|
|
|
|
programdata_address,
|
|
|
|
} = program_account.borrow().state()?
|
|
|
|
{
|
|
|
|
if let Some(account) = invoke_context.get_account(&programdata_address) {
|
|
|
|
Some((programdata_address, account))
|
2020-12-22 09:26:55 -08:00
|
|
|
} else {
|
2021-01-28 10:04:54 -08:00
|
|
|
ic_msg!(
|
|
|
|
invoke_context,
|
2021-04-19 09:48:48 -07:00
|
|
|
"Unknown upgradeable programdata account {}",
|
|
|
|
programdata_address,
|
2021-01-28 10:04:54 -08:00
|
|
|
);
|
2020-12-22 09:26:55 -08:00
|
|
|
return Err(InstructionError::MissingAccount);
|
|
|
|
}
|
2020-12-17 15:39:49 -08:00
|
|
|
} else {
|
2021-04-19 09:48:48 -07:00
|
|
|
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);
|
2021-01-28 10:04:54 -08:00
|
|
|
}
|
|
|
|
(
|
|
|
|
message,
|
2021-04-19 09:48:48 -07:00
|
|
|
executable_accounts,
|
2021-01-28 10:04:54 -08:00
|
|
|
accounts,
|
2021-04-19 09:48:48 -07:00
|
|
|
keyed_account_indices_reordered,
|
2021-03-10 23:04:00 -08:00
|
|
|
caller_write_privileges,
|
2021-03-30 10:05:09 -07:00
|
|
|
invoke_context.is_feature_active(&demote_sysvar_write_locks::id()),
|
2021-01-28 10:04:54 -08:00
|
|
|
)
|
|
|
|
};
|
2020-12-14 15:35:10 -08:00
|
|
|
|
2021-01-28 10:04:54 -08:00
|
|
|
#[allow(clippy::deref_addrof)]
|
2020-12-14 15:35:10 -08:00
|
|
|
MessageProcessor::process_cross_program_instruction(
|
|
|
|
&message,
|
2021-04-19 09:48:48 -07:00
|
|
|
&executable_accounts,
|
2020-12-14 15:35:10 -08:00
|
|
|
&accounts,
|
2021-03-10 23:04:00 -08:00
|
|
|
&caller_write_privileges,
|
2021-01-28 10:04:54 -08:00
|
|
|
*(&mut *(invoke_context.borrow_mut())),
|
2020-12-14 15:35:10 -08:00
|
|
|
)?;
|
|
|
|
|
|
|
|
// Copy results back to caller
|
|
|
|
|
2021-01-28 10:04:54 -08:00
|
|
|
{
|
|
|
|
let invoke_context = invoke_context.borrow();
|
2021-04-19 09:48:48 -07:00
|
|
|
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)
|
2021-04-27 07:12:17 -07:00
|
|
|
&& !src_keyed_account.executable()
|
2021-04-19 09:48:48 -07:00
|
|
|
{
|
|
|
|
if dst_keyed_account.data_len()? != src_keyed_account.data().len()
|
|
|
|
&& dst_keyed_account.data_len()? != 0
|
2021-01-28 10:04:54 -08:00
|
|
|
{
|
|
|
|
// 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());
|
2021-04-23 10:34:22 -07:00
|
|
|
dst_keyed_account
|
|
|
|
.try_account_ref_mut()?
|
2021-04-26 10:06:40 -07:00
|
|
|
.set_owner(*src_keyed_account.owner());
|
2021-04-19 09:48:48 -07:00
|
|
|
dst_keyed_account
|
2021-03-11 14:40:45 -08:00
|
|
|
.try_account_ref_mut()?
|
2021-04-20 14:41:16 -07:00
|
|
|
.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
|
2020-05-20 09:24:57 -07:00
|
|
|
/// 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,
|
2021-03-10 23:04:00 -08:00
|
|
|
executable_accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)],
|
2021-03-09 13:06:07 -08:00
|
|
|
accounts: &[Rc<RefCell<AccountSharedData>>],
|
2021-03-10 23:04:00 -08:00
|
|
|
caller_write_privileges: &[bool],
|
2020-04-28 14:33:56 -07:00
|
|
|
invoke_context: &mut dyn InvokeContext,
|
|
|
|
) -> Result<(), InstructionError> {
|
2020-10-12 13:40:04 -07:00
|
|
|
if let Some(instruction) = message.instructions.get(0) {
|
2020-12-23 19:04:48 -08:00
|
|
|
let program_id = instruction.program_id(&message.account_keys);
|
|
|
|
|
2020-10-12 13:40:04 -07:00
|
|
|
// Verify the calling program hasn't misbehaved
|
2021-01-22 15:28:01 -08:00
|
|
|
invoke_context.verify_and_update(
|
|
|
|
message,
|
|
|
|
instruction,
|
|
|
|
accounts,
|
2021-03-10 23:04:00 -08:00
|
|
|
Some(caller_write_privileges),
|
2021-01-22 15:28:01 -08:00
|
|
|
)?;
|
2021-04-19 09:48:48 -07:00
|
|
|
|
2020-10-12 13:40:04 -07:00
|
|
|
// Construct keyed accounts
|
2021-03-30 10:05:09 -07:00
|
|
|
let keyed_accounts = Self::create_keyed_accounts(
|
|
|
|
message,
|
|
|
|
instruction,
|
|
|
|
executable_accounts,
|
|
|
|
accounts,
|
2021-04-19 09:48:48 -07:00
|
|
|
invoke_context.is_feature_active(&demote_sysvar_write_locks::id()),
|
2021-03-30 10:05:09 -07:00
|
|
|
);
|
2020-10-12 13:40:04 -07:00
|
|
|
|
|
|
|
// Invoke callee
|
2021-04-19 09:48:48 -07:00
|
|
|
invoke_context.push(program_id, &keyed_accounts)?;
|
2020-10-30 01:10:13 -07:00
|
|
|
|
|
|
|
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(
|
2020-12-23 19:04:48 -08:00
|
|
|
program_id,
|
2020-10-30 01:10:13 -07:00
|
|
|
&instruction.data,
|
|
|
|
invoke_context,
|
|
|
|
);
|
2020-10-12 13:40:04 -07:00
|
|
|
if result.is_ok() {
|
|
|
|
// Verify the called program has not misbehaved
|
2021-01-22 15:28:01 -08:00
|
|
|
result = invoke_context.verify_and_update(message, instruction, accounts, None);
|
2020-10-12 13:40:04 -07:00
|
|
|
}
|
2020-04-28 14:33:56 -07:00
|
|
|
|
2021-04-19 09:48:48 -07:00
|
|
|
// Restore previous state
|
|
|
|
invoke_context.pop();
|
2020-10-12 13:40:04 -07:00
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-05 16:17:31 -08: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>>],
|
2020-03-05 16:17:31 -08:00
|
|
|
) -> Vec<PreAccount> {
|
2021-02-18 07:37:24 -08:00
|
|
|
let mut pre_accounts = Vec::with_capacity(instruction.accounts.len());
|
2020-03-05 16:17:31 -08:00
|
|
|
{
|
|
|
|
let mut work = |_unique_index: usize, account_index: usize| {
|
2020-04-28 14:33:56 -07:00
|
|
|
let key = &message.account_keys[account_index];
|
2020-03-05 16:17:31 -08:00
|
|
|
let account = accounts[account_index].borrow();
|
2021-03-10 09:48:41 -08:00
|
|
|
pre_accounts.push(PreAccount::new(key, &account));
|
2020-03-05 16:17:31 -08:00
|
|
|
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
|
2020-01-22 09:11:56 -08:00
|
|
|
pub fn verify_account_references(
|
2021-03-10 23:04:00 -08:00
|
|
|
accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)],
|
2020-01-22 09:11:56 -08:00
|
|
|
) -> Result<(), InstructionError> {
|
2020-04-28 14:33:56 -07:00
|
|
|
for (_, account) in accounts.iter() {
|
2020-01-22 09:11:56 -08:00
|
|
|
account
|
|
|
|
.try_borrow_mut()
|
|
|
|
.map_err(|_| InstructionError::AccountBorrowOutstanding)?;
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-02-21 13:28:35 -08:00
|
|
|
/// Verify the results of an instruction
|
2020-02-21 11:30:00 -08:00
|
|
|
pub fn verify(
|
2020-02-21 13:28:35 -08:00
|
|
|
message: &Message,
|
|
|
|
instruction: &CompiledInstruction,
|
2020-03-05 16:17:31 -08:00
|
|
|
pre_accounts: &[PreAccount],
|
2021-03-10 23:04:00 -08:00
|
|
|
executable_accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)],
|
2021-03-09 13:06:07 -08:00
|
|
|
accounts: &[Rc<RefCell<AccountSharedData>>],
|
2020-05-26 01:02:31 -07:00
|
|
|
rent: &Rent,
|
2021-03-03 15:07:45 -08:00
|
|
|
timings: &mut ExecuteDetailsTimings,
|
2021-03-30 10:05:09 -07:00
|
|
|
demote_sysvar_write_locks: bool,
|
2020-02-21 11:30:00 -08:00
|
|
|
) -> Result<(), InstructionError> {
|
2020-04-28 14:33:56 -07:00
|
|
|
// Verify all executable accounts have zero outstanding refs
|
|
|
|
Self::verify_account_references(executable_accounts)?;
|
2020-02-21 11:30:00 -08:00
|
|
|
|
|
|
|
// Verify the per-account instruction results
|
|
|
|
let (mut pre_sum, mut post_sum) = (0_u128, 0_u128);
|
2020-03-05 16:17:31 -08:00
|
|
|
{
|
|
|
|
let program_id = instruction.program_id(&message.account_keys);
|
|
|
|
let mut work = |unique_index: usize, account_index: usize| {
|
2021-03-10 23:04:00 -08:00
|
|
|
{
|
|
|
|
// Verify account has no outstanding references
|
|
|
|
let _ = accounts[account_index]
|
|
|
|
.try_borrow_mut()
|
|
|
|
.map_err(|_| InstructionError::AccountBorrowOutstanding)?;
|
|
|
|
}
|
|
|
|
let account = accounts[account_index].borrow();
|
2021-01-22 15:28:01 -08:00
|
|
|
pre_accounts[unique_index].verify(
|
|
|
|
&program_id,
|
2021-03-30 10:05:09 -07:00
|
|
|
message.is_writable(account_index, demote_sysvar_write_locks),
|
2021-01-22 15:28:01 -08:00
|
|
|
rent,
|
|
|
|
&account,
|
2021-03-03 15:07:45 -08:00
|
|
|
timings,
|
2021-03-31 16:02:59 -07:00
|
|
|
true,
|
2021-01-22 15:28:01 -08:00
|
|
|
)?;
|
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());
|
2020-03-05 16:17:31 -08:00
|
|
|
Ok(())
|
|
|
|
};
|
2020-03-12 09:08:39 -07:00
|
|
|
instruction.visit_each_account(&mut work)?;
|
2020-02-21 11:30:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify that the total sum of all the lamports did not change
|
|
|
|
if pre_sum != post_sum {
|
|
|
|
return Err(InstructionError::UnbalancedInstruction);
|
|
|
|
}
|
|
|
|
Ok(())
|
2019-11-05 10:57:32 -08:00
|
|
|
}
|
|
|
|
|
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,
|
2021-03-10 23:04:00 -08:00
|
|
|
caller_write_privileges: Option<&[bool]>,
|
2021-03-03 15:07:45 -08:00
|
|
|
timings: &mut ExecuteDetailsTimings,
|
2021-03-30 10:05:09 -07:00
|
|
|
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| {
|
2020-11-29 02:06:43 -08:00
|
|
|
if account_index < message.account_keys.len() && account_index < accounts.len() {
|
|
|
|
let key = &message.account_keys[account_index];
|
|
|
|
let account = &accounts[account_index];
|
2021-03-10 23:04:00 -08:00
|
|
|
let is_writable = if let Some(caller_write_privileges) = caller_write_privileges {
|
|
|
|
caller_write_privileges[account_index]
|
2021-01-22 15:28:01 -08:00
|
|
|
} else {
|
2021-03-30 10:05:09 -07:00
|
|
|
message.is_writable(account_index, demote_sysvar_write_locks)
|
2021-01-22 15:28:01 -08:00
|
|
|
};
|
2020-11-29 02:06:43 -08:00
|
|
|
// Find the matching PreAccount
|
|
|
|
for pre_account in pre_accounts.iter_mut() {
|
2021-04-19 09:48:48 -07:00
|
|
|
if key == pre_account.key() {
|
2021-03-10 23:04:00 -08:00
|
|
|
{
|
|
|
|
// Verify account has no outstanding references
|
|
|
|
let _ = account
|
|
|
|
.try_borrow_mut()
|
|
|
|
.map_err(|_| InstructionError::AccountBorrowOutstanding)?;
|
|
|
|
}
|
|
|
|
let account = account.borrow();
|
2021-03-31 16:02:59 -07:00
|
|
|
pre_account.verify(
|
|
|
|
&program_id,
|
|
|
|
is_writable,
|
|
|
|
&rent,
|
|
|
|
&account,
|
|
|
|
timings,
|
|
|
|
false,
|
|
|
|
)?;
|
2020-11-29 02:06:43 -08:00
|
|
|
pre_sum += u128::from(pre_account.lamports());
|
2021-04-29 08:44:46 -07:00
|
|
|
post_sum += u128::from(account.lamports());
|
2021-04-27 07:12:17 -07:00
|
|
|
if is_writable && !account.executable() {
|
2021-03-10 23:04:00 -08:00
|
|
|
pre_account.update(&account);
|
|
|
|
}
|
2020-11-29 02:06:43 -08:00
|
|
|
return Ok(());
|
|
|
|
}
|
2020-04-28 14:33:56 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(InstructionError::MissingAccount)
|
|
|
|
};
|
|
|
|
instruction.visit_each_account(&mut work)?;
|
2020-11-29 02:06:43 -08:00
|
|
|
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(())
|
|
|
|
}
|
|
|
|
|
2019-03-15 19:48:11 -07:00
|
|
|
/// 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(_).
|
2020-09-20 10:58:12 -07:00
|
|
|
#[allow(clippy::too_many_arguments)]
|
2019-03-15 19:48:11 -07:00
|
|
|
fn execute_instruction(
|
|
|
|
&self,
|
2019-03-29 09:05:06 -07:00
|
|
|
message: &Message,
|
2019-04-02 15:02:57 -07:00
|
|
|
instruction: &CompiledInstruction,
|
2021-03-10 23:04:00 -08:00
|
|
|
executable_accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)],
|
2021-03-09 13:06:07 -08:00
|
|
|
accounts: &[Rc<RefCell<AccountSharedData>>],
|
2021-03-10 23:04:00 -08:00
|
|
|
account_deps: &[(Pubkey, Rc<RefCell<AccountSharedData>>)],
|
2020-03-31 10:07:38 -07:00
|
|
|
rent_collector: &RentCollector,
|
2020-06-13 13:20:08 -07:00
|
|
|
log_collector: Option<Rc<LogCollector>>,
|
2020-09-14 17:42:37 -07:00
|
|
|
executors: Rc<RefCell<Executors>>,
|
2020-09-24 07:36:22 -07:00
|
|
|
instruction_recorder: Option<InstructionRecorder>,
|
2020-09-19 12:17:46 -07:00
|
|
|
instruction_index: usize,
|
2020-09-29 14:36:30 -07:00
|
|
|
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-03-30 10:05:09 -07:00
|
|
|
demote_sysvar_write_locks: bool,
|
2021-04-12 16:04:57 -07:00
|
|
|
account_db: Arc<Accounts>,
|
|
|
|
ancestors: &Ancestors,
|
2019-03-15 19:48:11 -07:00
|
|
|
) -> Result<(), InstructionError> {
|
2020-09-19 12:17:46 -07:00
|
|
|
// Fixup the special instructions key if present
|
|
|
|
// before the account pre-values are taken care of
|
2020-09-29 14:36:30 -07:00
|
|
|
if feature_set.is_active(&instructions_sysvar_enabled::id()) {
|
2020-09-20 10:58:12 -07:00
|
|
|
for (i, key) in message.account_keys.iter().enumerate() {
|
2021-04-06 00:08:03 -07:00
|
|
|
if instructions::check_id(key) {
|
2020-09-20 10:58:12 -07:00
|
|
|
let mut mut_account_ref = accounts[i].borrow_mut();
|
2021-04-06 00:08:03 -07:00
|
|
|
instructions::store_current_index(
|
2021-03-10 13:28:03 -08:00
|
|
|
mut_account_ref.data_as_mut_slice(),
|
2020-09-20 10:58:12 -07:00
|
|
|
instruction_index as u16,
|
|
|
|
);
|
|
|
|
break;
|
|
|
|
}
|
2020-09-19 12:17:46 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-23 19:04:48 -08:00
|
|
|
let program_id = instruction.program_id(&message.account_keys);
|
2020-04-28 14:33:56 -07:00
|
|
|
let mut invoke_context = ThisInvokeContext::new(
|
2020-12-23 19:04:48 -08:00
|
|
|
program_id,
|
2020-04-28 14:33:56 -07:00
|
|
|
rent_collector.rent,
|
2021-04-19 09:48:48 -07:00
|
|
|
message,
|
|
|
|
instruction,
|
2021-03-10 23:04:00 -08:00
|
|
|
executable_accounts,
|
2021-04-19 09:48:48 -07:00
|
|
|
accounts,
|
2020-12-22 09:26:55 -08:00
|
|
|
account_deps,
|
2020-10-29 09:57:14 -07:00
|
|
|
&self.programs,
|
2020-06-06 10:18:28 -07:00
|
|
|
log_collector,
|
2020-10-28 13:16:13 -07:00
|
|
|
bpf_compute_budget,
|
2020-09-14 17:42:37 -07:00
|
|
|
executors,
|
2020-09-24 07:36:22 -07:00
|
|
|
instruction_recorder,
|
2020-09-29 14:36:30 -07:00
|
|
|
feature_set,
|
2021-04-12 16:04:57 -07:00
|
|
|
account_db,
|
|
|
|
ancestors,
|
2020-04-28 14:33:56 -07:00
|
|
|
);
|
2021-04-19 09:48:48 -07:00
|
|
|
self.process_instruction(program_id, &instruction.data, &mut invoke_context)?;
|
2020-02-21 11:30:00 -08:00
|
|
|
Self::verify(
|
2020-02-21 13:28:35 -08:00
|
|
|
message,
|
|
|
|
instruction,
|
2020-04-28 14:33:56 -07:00
|
|
|
&invoke_context.pre_accounts,
|
2020-02-21 11:30:00 -08:00
|
|
|
executable_accounts,
|
2020-03-05 16:17:31 -08:00
|
|
|
accounts,
|
2020-05-26 01:02:31 -07:00
|
|
|
&rent_collector.rent,
|
2021-03-03 15:07:45 -08:00
|
|
|
timings,
|
2021-03-30 10:05:09 -07:00
|
|
|
demote_sysvar_write_locks,
|
2020-02-21 11:30:00 -08:00
|
|
|
)?;
|
2021-03-03 15:07:45 -08:00
|
|
|
|
|
|
|
timings.accumulate(&invoke_context.timings);
|
|
|
|
|
2019-03-15 19:48:11 -07:00
|
|
|
Ok(())
|
2019-03-07 09:35:28 -08:00
|
|
|
}
|
|
|
|
|
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
|
2019-03-15 19:48:11 -07:00
|
|
|
/// The accounts are committed back to the bank only if every instruction succeeds
|
2020-09-24 07:36:22 -07:00
|
|
|
#[allow(clippy::too_many_arguments)]
|
2021-03-10 23:04:00 -08:00
|
|
|
#[allow(clippy::type_complexity)]
|
2019-03-29 09:05:06 -07:00
|
|
|
pub fn process_message(
|
2019-03-15 19:48:11 -07:00
|
|
|
&self,
|
2019-03-29 09:05:06 -07:00
|
|
|
message: &Message,
|
2021-03-10 23:04:00 -08:00
|
|
|
loaders: &[Vec<(Pubkey, Rc<RefCell<AccountSharedData>>)>],
|
2021-03-09 13:06:07 -08:00
|
|
|
accounts: &[Rc<RefCell<AccountSharedData>>],
|
2021-03-10 23:04:00 -08:00
|
|
|
account_deps: &[(Pubkey, Rc<RefCell<AccountSharedData>>)],
|
2020-03-31 10:07:38 -07:00
|
|
|
rent_collector: &RentCollector,
|
2020-06-13 13:20:08 -07:00
|
|
|
log_collector: Option<Rc<LogCollector>>,
|
2020-09-14 17:42:37 -07:00
|
|
|
executors: Rc<RefCell<Executors>>,
|
2020-09-25 05:42:28 -07:00
|
|
|
instruction_recorders: Option<&[InstructionRecorder]>,
|
2020-09-29 14:36:30 -07:00
|
|
|
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,
|
2019-03-15 19:48:11 -07:00
|
|
|
) -> Result<(), TransactionError> {
|
2021-03-30 10:05:09 -07:00
|
|
|
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() {
|
2020-09-25 05:42:28 -07:00
|
|
|
let instruction_recorder = instruction_recorders
|
|
|
|
.as_ref()
|
|
|
|
.map(|recorders| recorders[instruction_index].clone());
|
2020-03-31 10:07:38 -07:00
|
|
|
self.execute_instruction(
|
|
|
|
message,
|
|
|
|
instruction,
|
2020-06-03 21:16:15 -07:00
|
|
|
&loaders[instruction_index],
|
2020-03-31 10:07:38 -07:00
|
|
|
accounts,
|
2020-12-22 09:26:55 -08:00
|
|
|
account_deps,
|
2020-03-31 10:07:38 -07:00
|
|
|
rent_collector,
|
2020-06-13 13:20:08 -07:00
|
|
|
log_collector.clone(),
|
2020-09-14 17:42:37 -07:00
|
|
|
executors.clone(),
|
2020-09-24 07:36:22 -07:00
|
|
|
instruction_recorder,
|
2020-09-19 12:17:46 -07:00
|
|
|
instruction_index,
|
2020-09-29 14:36:30 -07:00
|
|
|
feature_set.clone(),
|
2020-10-28 13:16:13 -07:00
|
|
|
bpf_compute_budget,
|
2021-03-03 15:07:45 -08:00
|
|
|
timings,
|
2021-03-30 10:05:09 -07:00
|
|
|
demote_sysvar_write_locks,
|
2021-04-12 16:04:57 -07:00
|
|
|
account_db.clone(),
|
|
|
|
ancestors,
|
2020-03-31 10:07:38 -07:00
|
|
|
)
|
|
|
|
.map_err(|err| TransactionError::InstructionError(instruction_index as u8, err))?;
|
2019-03-11 15:35:25 -07:00
|
|
|
}
|
2019-03-15 19:48:11 -07:00
|
|
|
Ok(())
|
|
|
|
}
|
2019-03-11 15:35:25 -07:00
|
|
|
}
|
|
|
|
|
2019-02-18 22:26:22 -08:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
2020-01-28 17:03:20 -08:00
|
|
|
use solana_sdk::{
|
2021-03-11 16:09:04 -08:00
|
|
|
account::Account,
|
2020-01-28 17:03:20 -08:00
|
|
|
instruction::{AccountMeta, Instruction, InstructionError},
|
|
|
|
message::Message,
|
2021-03-24 23:23:20 -07:00
|
|
|
native_loader::create_loadable_account_for_test,
|
2020-01-28 17:03:20 -08:00
|
|
|
};
|
2019-02-18 22:26:22 -08:00
|
|
|
|
2020-04-28 14:33:56 -07:00
|
|
|
#[test]
|
|
|
|
fn test_invoke_context() {
|
|
|
|
const MAX_DEPTH: usize = 10;
|
2021-04-19 09:48:48 -07:00
|
|
|
let mut invoke_stack = vec![];
|
2020-04-28 14:33:56 -07:00
|
|
|
let mut keys = vec![];
|
|
|
|
let mut accounts = vec![];
|
2021-04-19 09:48:48 -07:00
|
|
|
let mut metas = vec![];
|
2020-04-28 14:33:56 -07:00
|
|
|
for i in 0..MAX_DEPTH {
|
2021-04-19 09:48:48 -07:00
|
|
|
invoke_stack.push(solana_sdk::pubkey::new_rand());
|
2020-10-19 12:12:08 -07:00
|
|
|
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,
|
2021-04-19 09:48:48 -07:00
|
|
|
&invoke_stack[i],
|
2020-04-28 14:33:56 -07:00
|
|
|
))));
|
2021-04-19 09:48:48 -07:00
|
|
|
metas.push(AccountMeta::new(keys[i], false));
|
2020-04-28 14:33:56 -07:00
|
|
|
}
|
2021-04-19 09:48:48 -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));
|
2020-11-29 02:06:43 -08:00
|
|
|
}
|
|
|
|
|
2021-04-19 09:48:48 -07:00
|
|
|
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();
|
2020-07-29 15:29:52 -07:00
|
|
|
let mut invoke_context = ThisInvokeContext::new(
|
2021-04-19 09:48:48 -07:00
|
|
|
&invoke_stack[0],
|
2020-07-29 15:29:52 -07:00
|
|
|
Rent::default(),
|
2021-04-19 09:48:48 -07:00
|
|
|
&message,
|
|
|
|
&message.instructions[0],
|
2020-10-29 09:57:14 -07:00
|
|
|
&[],
|
2021-04-19 09:48:48 -07:00
|
|
|
&accounts,
|
2020-12-22 09:26:55 -08:00
|
|
|
&[],
|
2021-03-10 23:04:00 -08:00
|
|
|
&[],
|
2020-07-29 15:29:52 -07:00
|
|
|
None,
|
2020-10-28 13:16:13 -07:00
|
|
|
BpfComputeBudget::default(),
|
2020-09-14 17:42:37 -07:00
|
|
|
Rc::new(RefCell::new(Executors::default())),
|
2020-09-24 07:36:22 -07:00
|
|
|
None,
|
2020-09-29 20:18:28 -07:00
|
|
|
Arc::new(FeatureSet::all_enabled()),
|
2021-04-12 16:04:57 -07:00
|
|
|
Arc::new(Accounts::default()),
|
|
|
|
&ancestors,
|
2020-07-29 15:29:52 -07:00
|
|
|
);
|
2020-04-28 14:33:56 -07:00
|
|
|
|
|
|
|
// Check call depth increases and has a limit
|
|
|
|
let mut depth_reached = 1;
|
2021-04-19 09:48:48 -07:00
|
|
|
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),
|
|
|
|
];
|
2020-06-24 13:52:38 -07:00
|
|
|
let message = Message::new(
|
2021-03-03 21:46:48 -08:00
|
|
|
&[Instruction::new_with_bytes(
|
2021-04-19 09:48:48 -07:00
|
|
|
invoke_stack[owned_index],
|
2021-03-03 21:46:48 -08:00
|
|
|
&[0],
|
|
|
|
metas,
|
|
|
|
)],
|
2020-05-15 12:23:09 -07:00
|
|
|
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(
|
2020-11-29 02:06:43 -08:00
|
|
|
1,
|
|
|
|
1,
|
|
|
|
&solana_sdk::pubkey::Pubkey::default(),
|
|
|
|
))));
|
2020-04-28 14:33:56 -07:00
|
|
|
invoke_context
|
2021-01-22 15:28:01 -08:00
|
|
|
.verify_and_update(&message, &message.instructions[0], &these_accounts, None)
|
2020-04-28 14:33:56 -07:00
|
|
|
.unwrap();
|
|
|
|
assert_eq!(
|
2020-12-23 10:18:14 -08:00
|
|
|
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],
|
2021-01-22 15:28:01 -08:00
|
|
|
None
|
2020-04-28 14:33:56 -07:00
|
|
|
),
|
|
|
|
Err(InstructionError::ExternalAccountDataModified)
|
|
|
|
);
|
2020-12-23 10:18:14 -08:00
|
|
|
assert_eq!(
|
|
|
|
invoke_context.pre_accounts[not_owned_index]
|
|
|
|
.account
|
|
|
|
.borrow()
|
2021-03-09 14:31:33 -08:00
|
|
|
.data()[0],
|
2020-12-23 10:18:14 -08:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-24 11:06:00 -07:00
|
|
|
#[test]
|
|
|
|
fn test_is_zeroed() {
|
2020-02-21 11:30:00 -08:00
|
|
|
const ZEROS_LEN: usize = 1024;
|
2019-10-24 11:06:00 -07:00
|
|
|
let mut buf = [0; ZEROS_LEN];
|
2021-05-19 07:31:47 -07:00
|
|
|
assert!(PreAccount::is_zeroed(&buf));
|
2019-10-24 11:06:00 -07:00
|
|
|
buf[0] = 1;
|
2021-05-19 07:31:47 -07:00
|
|
|
assert!(!PreAccount::is_zeroed(&buf));
|
2019-10-24 11:06:00 -07:00
|
|
|
|
|
|
|
let mut buf = [0; ZEROS_LEN - 1];
|
2021-05-19 07:31:47 -07:00
|
|
|
assert!(PreAccount::is_zeroed(&buf));
|
2019-10-24 11:06:00 -07:00
|
|
|
buf[0] = 1;
|
2021-05-19 07:31:47 -07:00
|
|
|
assert!(!PreAccount::is_zeroed(&buf));
|
2019-10-24 11:06:00 -07:00
|
|
|
|
|
|
|
let mut buf = [0; ZEROS_LEN + 1];
|
2021-05-19 07:31:47 -07:00
|
|
|
assert!(PreAccount::is_zeroed(&buf));
|
2019-10-24 11:06:00 -07:00
|
|
|
buf[0] = 1;
|
2021-05-19 07:31:47 -07:00
|
|
|
assert!(!PreAccount::is_zeroed(&buf));
|
2019-10-24 11:06:00 -07:00
|
|
|
|
|
|
|
let buf = vec![];
|
2021-05-19 07:31:47 -07:00
|
|
|
assert!(PreAccount::is_zeroed(&buf));
|
2019-10-24 11:06:00 -07:00
|
|
|
}
|
|
|
|
|
2019-02-18 22:26:22 -08:00
|
|
|
#[test]
|
2020-01-22 09:11:56 -08:00
|
|
|
fn test_verify_account_references() {
|
2020-10-19 12:23:14 -07:00
|
|
|
let accounts = vec![(
|
|
|
|
solana_sdk::pubkey::new_rand(),
|
2021-03-10 23:04:00 -08:00
|
|
|
Rc::new(RefCell::new(AccountSharedData::default())),
|
2020-10-19 12:23:14 -07:00
|
|
|
)];
|
2020-01-22 09:11:56 -08:00
|
|
|
|
2020-04-28 14:33:56 -07:00
|
|
|
assert!(MessageProcessor::verify_account_references(&accounts).is_ok());
|
2020-01-22 09:11:56 -08:00
|
|
|
|
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)
|
|
|
|
);
|
2019-02-18 22:26:22 -08:00
|
|
|
}
|
|
|
|
|
2020-05-26 01:02:31 -07:00
|
|
|
struct Change {
|
2020-04-18 17:04:13 -07:00
|
|
|
program_id: Pubkey,
|
2021-01-22 15:28:01 -08:00
|
|
|
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
|
|
|
}
|
2020-05-26 01:02:31 -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(),
|
2021-01-22 15:28:01 -08:00
|
|
|
is_writable: true,
|
2020-04-18 17:04:13 -07:00
|
|
|
pre: PreAccount::new(
|
2020-10-19 12:12:08 -07:00
|
|
|
&solana_sdk::pubkey::new_rand(),
|
2021-03-11 16:09:04 -08:00
|
|
|
&AccountSharedData::from(Account {
|
2020-04-18 17:04:13 -07:00
|
|
|
owner: *owner,
|
|
|
|
lamports: std::u64::MAX,
|
|
|
|
data: vec![],
|
2021-03-11 16:09:04 -08:00
|
|
|
..Account::default()
|
|
|
|
}),
|
2020-04-18 17:04:13 -07:00
|
|
|
),
|
2021-03-11 16:09:04 -08:00
|
|
|
post: AccountSharedData::from(Account {
|
2020-04-18 17:04:13 -07:00
|
|
|
owner: *owner,
|
|
|
|
lamports: std::u64::MAX,
|
2021-03-11 16:09:04 -08:00
|
|
|
..Account::default()
|
|
|
|
}),
|
2020-04-18 17:04:13 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
pub fn read_only(mut self) -> Self {
|
2021-01-22 15:28:01 -08:00
|
|
|
self.is_writable = false;
|
2020-04-18 17:04:13 -07:00
|
|
|
self
|
|
|
|
}
|
|
|
|
pub fn executable(mut self, pre: bool, post: bool) -> Self {
|
2021-04-28 07:07:43 -07:00
|
|
|
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 {
|
2021-04-22 11:53:06 -07:00
|
|
|
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 {
|
2021-04-23 10:34:22 -07:00
|
|
|
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 {
|
2021-03-11 14:40:45 -08:00
|
|
|
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 {
|
2021-04-27 11:51:13 -07:00
|
|
|
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> {
|
2021-01-22 15:28:01 -08:00
|
|
|
self.pre.verify(
|
|
|
|
&self.program_id,
|
2021-03-10 09:48:41 -08:00
|
|
|
self.is_writable,
|
2021-01-22 15:28:01 -08:00
|
|
|
&self.rent,
|
|
|
|
&self.post,
|
2021-03-03 15:07:45 -08:00
|
|
|
&mut ExecuteDetailsTimings::default(),
|
2021-03-31 16:02:59 -07:00
|
|
|
false,
|
2021-01-22 15:28:01 -08:00
|
|
|
)
|
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();
|
2020-10-19 12:12:08 -07:00
|
|
|
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(),
|
2019-07-29 15:29:20 -07:00
|
|
|
Err(InstructionError::ModifiedProgramId),
|
2019-11-05 08:38:35 -08:00
|
|
|
"system program should not be able to change the account owner of a read-only account"
|
2019-07-29 15:29:20 -07:00
|
|
|
);
|
2019-10-24 11:06:00 -07:00
|
|
|
assert_eq!(
|
2020-04-18 17:04:13 -07:00
|
|
|
Change::new(&mallory_program_id, &system_program_id)
|
|
|
|
.owner(&alice_program_id)
|
|
|
|
.verify(),
|
2019-10-24 11:06:00 -07:00
|
|
|
Err(InstructionError::ModifiedProgramId),
|
|
|
|
"system program should not be able to change the account owner of a non-system account"
|
|
|
|
);
|
2019-07-29 15:29:20 -07:00
|
|
|
assert_eq!(
|
2020-04-18 17:04:13 -07:00
|
|
|
Change::new(&mallory_program_id, &mallory_program_id)
|
|
|
|
.owner(&alice_program_id)
|
|
|
|
.verify(),
|
2019-10-24 11:06:00 -07:00
|
|
|
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(),
|
2019-10-24 11:06:00 -07:00
|
|
|
Ok(()),
|
|
|
|
"mallory should be able to change the account owner, if she leaves clear data"
|
|
|
|
);
|
2020-12-07 11:37:07 -08:00
|
|
|
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"
|
|
|
|
);
|
2019-10-24 11:06:00 -07:00
|
|
|
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(),
|
2019-03-13 10:46:49 -07:00
|
|
|
Err(InstructionError::ModifiedProgramId),
|
2019-10-24 11:06:00 -07:00
|
|
|
"mallory should not be able to inject data into the alice program"
|
2019-02-22 12:08:54 -08:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2019-11-18 14:01:14 -08:00
|
|
|
fn test_verify_account_changes_executable() {
|
2020-10-19 12:12:08 -07:00
|
|
|
let owner = solana_sdk::pubkey::new_rand();
|
|
|
|
let mallory_program_id = solana_sdk::pubkey::new_rand();
|
2019-07-29 15:29:20 -07:00
|
|
|
let system_program_id = system_program::id();
|
|
|
|
|
|
|
|
assert_eq!(
|
2020-03-27 16:43:25 -07:00
|
|
|
Change::new(&owner, &system_program_id)
|
|
|
|
.executable(false, true)
|
|
|
|
.verify(),
|
2019-10-24 11:06:00 -07:00
|
|
|
Err(InstructionError::ExecutableModified),
|
|
|
|
"system program can't change executable if system doesn't own the account"
|
2019-07-29 15:29:20 -07:00
|
|
|
);
|
|
|
|
assert_eq!(
|
2020-03-27 16:43:25 -07:00
|
|
|
Change::new(&owner, &system_program_id)
|
|
|
|
.executable(true, true)
|
|
|
|
.data(vec![1], vec![2])
|
|
|
|
.verify(),
|
2020-03-26 17:10:11 -07:00
|
|
|
Err(InstructionError::ExecutableDataModified),
|
|
|
|
"system program can't change executable data if system doesn't own the account"
|
|
|
|
);
|
|
|
|
assert_eq!(
|
2020-03-27 16:43:25 -07:00
|
|
|
Change::new(&owner, &owner).executable(false, true).verify(),
|
2019-07-29 15:29:20 -07:00
|
|
|
Ok(()),
|
2020-03-26 17:10:11 -07:00
|
|
|
"owner should be able to change executable"
|
2019-07-29 15:29:20 -07:00
|
|
|
);
|
|
|
|
assert_eq!(
|
2020-03-27 16:43:25 -07:00
|
|
|
Change::new(&owner, &owner)
|
|
|
|
.executable(false, true)
|
|
|
|
.read_only()
|
|
|
|
.verify(),
|
2019-07-29 15:29:20 -07:00
|
|
|
Err(InstructionError::ExecutableModified),
|
2020-03-26 17:10:11 -07:00
|
|
|
"owner can't modify executable of read-only accounts"
|
2019-07-29 15:29:20 -07:00
|
|
|
);
|
|
|
|
assert_eq!(
|
2020-03-27 16:43:25 -07:00
|
|
|
Change::new(&owner, &owner).executable(true, false).verify(),
|
2019-07-29 15:29:20 -07:00
|
|
|
Err(InstructionError::ExecutableModified),
|
2020-03-26 17:10:11 -07:00
|
|
|
"owner program can't reverse executable"
|
2019-07-29 15:29:20 -07:00
|
|
|
);
|
|
|
|
assert_eq!(
|
2020-03-27 16:43:25 -07:00
|
|
|
Change::new(&owner, &mallory_program_id)
|
|
|
|
.executable(false, true)
|
|
|
|
.verify(),
|
2019-07-29 15:29:20 -07:00
|
|
|
Err(InstructionError::ExecutableModified),
|
|
|
|
"malicious Mallory should not be able to change the account executable"
|
|
|
|
);
|
2020-03-26 17:10:11 -07:00
|
|
|
assert_eq!(
|
2020-03-27 16:43:25 -07:00
|
|
|
Change::new(&owner, &owner)
|
|
|
|
.executable(false, true)
|
|
|
|
.data(vec![1], vec![2])
|
|
|
|
.verify(),
|
2020-03-26 17:10:11 -07:00
|
|
|
Ok(()),
|
|
|
|
"account data can change in the same instruction that sets the bit"
|
|
|
|
);
|
|
|
|
assert_eq!(
|
2020-03-27 16:43:25 -07:00
|
|
|
Change::new(&owner, &owner)
|
|
|
|
.executable(true, true)
|
|
|
|
.data(vec![1], vec![2])
|
|
|
|
.verify(),
|
2020-03-26 17:10:11 -07:00
|
|
|
Err(InstructionError::ExecutableDataModified),
|
|
|
|
"owner should not be able to change an account's data once its marked executable"
|
|
|
|
);
|
2020-03-27 16:43:25 -07:00
|
|
|
assert_eq!(
|
|
|
|
Change::new(&owner, &owner)
|
|
|
|
.executable(true, true)
|
|
|
|
.lamports(1, 2)
|
|
|
|
.verify(),
|
|
|
|
Err(InstructionError::ExecutableLamportChange),
|
2020-06-17 20:54:52 -07:00
|
|
|
"owner should not be able to add lamports once marked executable"
|
2020-03-27 16:43:25 -07:00
|
|
|
);
|
|
|
|
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"
|
|
|
|
);
|
2020-03-31 10:07:38 -07:00
|
|
|
let data = vec![1; 100];
|
2020-04-28 14:33:56 -07:00
|
|
|
let min_lamports = Rent::default().minimum_balance(data.len());
|
2020-03-31 10:07:38 -07:00
|
|
|
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)
|
2020-05-15 09:35:43 -07:00
|
|
|
.data(data.clone(), data)
|
2020-03-31 10:07:38 -07:00
|
|
|
.verify(),
|
|
|
|
Err(InstructionError::ExecutableAccountNotRentExempt),
|
|
|
|
"owner should not be able to change an account's data once its marked executable"
|
|
|
|
);
|
2019-07-29 15:29:20 -07:00
|
|
|
}
|
|
|
|
|
2019-10-24 11:06:00 -07:00
|
|
|
#[test]
|
2019-11-18 14:01:14 -08:00
|
|
|
fn test_verify_account_changes_data_len() {
|
2020-10-19 12:12:08 -07:00
|
|
|
let alice_program_id = solana_sdk::pubkey::new_rand();
|
2020-04-18 17:04:13 -07:00
|
|
|
|
2019-10-24 11:06:00 -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(),
|
2019-10-24 11:06:00 -07:00
|
|
|
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"
|
2019-10-24 11:06:00 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-07-29 15:29:20 -07:00
|
|
|
#[test]
|
2019-11-18 14:01:14 -08:00
|
|
|
fn test_verify_account_changes_data() {
|
2020-10-19 12:12:08 -07:00
|
|
|
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
|
|
|
|
2019-07-29 15:29:20 -07: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(),
|
2019-07-29 15:29:20 -07:00
|
|
|
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(),
|
2019-03-14 09:48:27 -07:00
|
|
|
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
|
|
|
);
|
2019-06-10 19:50:02 -07: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(),
|
2019-11-05 08:38:35 -08:00
|
|
|
Err(InstructionError::ReadonlyDataModified),
|
2019-10-25 21:47:16 -07:00
|
|
|
"alice isn't allowed to touch a CO account"
|
2019-06-10 19:50:02 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-08-26 11:04:20 -07:00
|
|
|
#[test]
|
2019-11-18 14:01:14 -08:00
|
|
|
fn test_verify_account_changes_rent_epoch() {
|
2020-10-19 12:12:08 -07:00
|
|
|
let alice_program_id = solana_sdk::pubkey::new_rand();
|
2019-08-26 11:04:20 -07:00
|
|
|
|
|
|
|
assert_eq!(
|
2020-04-18 17:04:13 -07:00
|
|
|
Change::new(&alice_program_id, &system_program::id()).verify(),
|
2019-08-26 11:04:20 -07:00
|
|
|
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(),
|
2019-08-26 11:04:20 -07:00
|
|
|
Err(InstructionError::RentEpochModified),
|
|
|
|
"no one touches rent_epoch"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-06-10 19:50:02 -07:00
|
|
|
#[test]
|
2019-11-18 14:01:14 -08:00
|
|
|
fn test_verify_account_changes_deduct_lamports_and_reassign_account() {
|
2020-10-19 12:12:08 -07:00
|
|
|
let alice_program_id = solana_sdk::pubkey::new_rand();
|
|
|
|
let bob_program_id = solana_sdk::pubkey::new_rand();
|
2019-10-24 11:06:00 -07:00
|
|
|
|
|
|
|
// 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",
|
|
|
|
);
|
2019-10-24 11:06:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2019-11-18 14:01:14 -08:00
|
|
|
fn test_verify_account_changes_lamports() {
|
2020-10-19 12:12:08 -07:00
|
|
|
let alice_program_id = solana_sdk::pubkey::new_rand();
|
2019-10-24 11:06:00 -07:00
|
|
|
|
2019-06-10 19:50:02 -07:00
|
|
|
assert_eq!(
|
2020-04-18 17:04:13 -07:00
|
|
|
Change::new(&alice_program_id, &system_program::id())
|
|
|
|
.lamports(42, 0)
|
|
|
|
.read_only()
|
|
|
|
.verify(),
|
2019-06-10 19:50:02 -07:00
|
|
|
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(),
|
2019-11-05 08:38:35 -08:00
|
|
|
Err(InstructionError::ReadonlyLamportChange),
|
2019-06-10 19:50:02 -07:00
|
|
|
"debit should fail, even if owning program"
|
|
|
|
);
|
2019-10-24 11:06:00 -07:00
|
|
|
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(),
|
2019-10-24 11:06:00 -07:00
|
|
|
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(),
|
2019-10-24 11:06:00 -07:00
|
|
|
Ok(()),
|
|
|
|
"system can spend (and change owner)"
|
|
|
|
);
|
2019-06-10 19:50:02 -07:00
|
|
|
}
|
|
|
|
|
2019-10-16 10:47:45 -07:00
|
|
|
#[test]
|
2019-11-18 14:01:14 -08:00
|
|
|
fn test_verify_account_changes_data_size_changed() {
|
2020-10-19 12:12:08 -07:00
|
|
|
let alice_program_id = solana_sdk::pubkey::new_rand();
|
2020-04-18 17:04:13 -07:00
|
|
|
|
2019-10-16 10:47:45 -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(),
|
2019-10-24 11:06:00 -07:00
|
|
|
Err(InstructionError::AccountDataSizeChanged),
|
|
|
|
"system program should not be able to change another program's account data size"
|
2019-10-16 10:47:45 -07:00
|
|
|
);
|
|
|
|
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(),
|
2019-10-16 10:47:45 -07:00
|
|
|
Err(InstructionError::AccountDataSizeChanged),
|
|
|
|
"non-system programs cannot change their data size"
|
|
|
|
);
|
2019-10-24 11:06:00 -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(),
|
2019-10-24 11:06:00 -07:00
|
|
|
Ok(()),
|
2020-06-17 20:54:52 -07:00
|
|
|
"system program should be able to change account data size"
|
2019-10-24 11:06:00 -07:00
|
|
|
);
|
2019-10-16 10:47:45 -07:00
|
|
|
}
|
|
|
|
|
2019-06-10 19:50:02 -07:00
|
|
|
#[test]
|
2019-11-05 08:38:35 -08:00
|
|
|
fn test_process_message_readonly_handling() {
|
2019-06-10 19:50:02 -07:00
|
|
|
#[derive(Serialize, Deserialize)]
|
|
|
|
enum MockSystemInstruction {
|
2019-11-05 08:38:35 -08:00
|
|
|
Correct,
|
|
|
|
AttemptCredit { lamports: u64 },
|
|
|
|
AttemptDataChange { data: u8 },
|
2019-06-10 19:50:02 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
fn mock_system_process_instruction(
|
|
|
|
_program_id: &Pubkey,
|
|
|
|
data: &[u8],
|
2021-04-19 09:48:48 -07:00
|
|
|
invoke_context: &mut dyn InvokeContext,
|
2019-06-10 19:50:02 -07:00
|
|
|
) -> Result<(), InstructionError> {
|
2021-04-19 09:48:48 -07:00
|
|
|
let keyed_accounts = invoke_context.get_keyed_accounts()?;
|
2019-06-10 19:50:02 -07:00
|
|
|
if let Ok(instruction) = bincode::deserialize(data) {
|
|
|
|
match instruction {
|
2019-11-05 08:38:35 -08:00
|
|
|
MockSystemInstruction::Correct => Ok(()),
|
|
|
|
MockSystemInstruction::AttemptCredit { lamports } => {
|
2021-05-03 08:45:15 -07:00
|
|
|
keyed_accounts[0]
|
|
|
|
.account
|
|
|
|
.borrow_mut()
|
|
|
|
.checked_sub_lamports(lamports)?;
|
2021-04-27 07:56:18 -07:00
|
|
|
keyed_accounts[1]
|
|
|
|
.account
|
|
|
|
.borrow_mut()
|
|
|
|
.checked_add_lamports(lamports)?;
|
2019-06-10 19:50:02 -07:00
|
|
|
Ok(())
|
|
|
|
}
|
2019-11-05 08:38:35 -08:00
|
|
|
// Change data in a read-only account
|
|
|
|
MockSystemInstruction::AttemptDataChange { data } => {
|
2021-03-11 14:40:45 -08:00
|
|
|
keyed_accounts[1].account.borrow_mut().set_data(vec![data]);
|
2019-06-10 19:50:02 -07:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Err(InstructionError::InvalidInstructionData)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let mock_system_program_id = Pubkey::new(&[2u8; 32]);
|
2020-03-31 10:07:38 -07:00
|
|
|
let rent_collector = RentCollector::default();
|
2019-06-10 19:50:02 -07:00
|
|
|
let mut message_processor = MessageProcessor::default();
|
2020-05-19 19:45:30 -07:00
|
|
|
message_processor.add_program(mock_system_program_id, mock_system_process_instruction);
|
2019-06-10 19:50:02 -07:00
|
|
|
|
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);
|
2019-06-10 19:50:02 -07:00
|
|
|
accounts.push(account);
|
2021-03-09 13:06:07 -08:00
|
|
|
let account = AccountSharedData::new_ref(0, 1, &mock_system_program_id);
|
2019-06-10 19:50:02 -07:00
|
|
|
accounts.push(account);
|
|
|
|
|
2021-03-10 23:04:00 -08:00
|
|
|
let mut loaders: Vec<Vec<(Pubkey, Rc<RefCell<AccountSharedData>>)>> = Vec::new();
|
2021-03-24 23:23:20 -07:00
|
|
|
let account = Rc::new(RefCell::new(create_loadable_account_for_test(
|
2021-03-10 23:04:00 -08:00
|
|
|
"mock_system_program",
|
|
|
|
)));
|
2019-09-24 04:09:53 -07:00
|
|
|
loaders.push(vec![(mock_system_program_id, account)]);
|
2019-06-10 19:50:02 -07:00
|
|
|
|
2020-09-14 17:42:37 -07:00
|
|
|
let executors = Rc::new(RefCell::new(Executors::default()));
|
2021-04-12 16:04:57 -07:00
|
|
|
let ancestors = Ancestors::default();
|
2020-09-14 17:42:37 -07:00
|
|
|
|
2020-10-19 12:12:08 -07:00
|
|
|
let from_pubkey = solana_sdk::pubkey::new_rand();
|
|
|
|
let to_pubkey = solana_sdk::pubkey::new_rand();
|
2019-06-10 19:50:02 -07:00
|
|
|
let account_metas = vec![
|
|
|
|
AccountMeta::new(from_pubkey, true),
|
2019-11-05 08:38:35 -08:00
|
|
|
AccountMeta::new_readonly(to_pubkey, false),
|
2019-06-10 19:50:02 -07:00
|
|
|
];
|
2020-06-24 13:52:38 -07:00
|
|
|
let message = Message::new(
|
2021-03-03 21:46:48 -08:00
|
|
|
&[Instruction::new_with_bincode(
|
2020-06-24 13:52:38 -07:00
|
|
|
mock_system_program_id,
|
|
|
|
&MockSystemInstruction::Correct,
|
|
|
|
account_metas.clone(),
|
|
|
|
)],
|
|
|
|
Some(&from_pubkey),
|
|
|
|
);
|
2019-06-10 19:50:02 -07:00
|
|
|
|
2020-09-14 17:42:37 -07:00
|
|
|
let result = message_processor.process_message(
|
|
|
|
&message,
|
|
|
|
&loaders,
|
|
|
|
&accounts,
|
2020-12-22 09:26:55 -08:00
|
|
|
&[],
|
2020-09-14 17:42:37 -07:00
|
|
|
&rent_collector,
|
|
|
|
None,
|
|
|
|
executors.clone(),
|
2020-09-24 07:36:22 -07:00
|
|
|
None,
|
2020-09-29 20:18:28 -07:00
|
|
|
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,
|
2020-09-14 17:42:37 -07:00
|
|
|
);
|
2019-06-10 19:50:02 -07:00
|
|
|
assert_eq!(result, Ok(()));
|
2021-05-03 08:45:54 -07:00
|
|
|
assert_eq!(accounts[0].borrow().lamports(), 100);
|
|
|
|
assert_eq!(accounts[1].borrow().lamports(), 0);
|
2019-06-10 19:50:02 -07:00
|
|
|
|
2020-06-24 13:52:38 -07:00
|
|
|
let message = Message::new(
|
2021-03-03 21:46:48 -08:00
|
|
|
&[Instruction::new_with_bincode(
|
2020-06-24 13:52:38 -07:00
|
|
|
mock_system_program_id,
|
|
|
|
&MockSystemInstruction::AttemptCredit { lamports: 50 },
|
|
|
|
account_metas.clone(),
|
|
|
|
)],
|
|
|
|
Some(&from_pubkey),
|
|
|
|
);
|
2019-06-10 19:50:02 -07:00
|
|
|
|
2020-09-14 17:42:37 -07:00
|
|
|
let result = message_processor.process_message(
|
|
|
|
&message,
|
|
|
|
&loaders,
|
|
|
|
&accounts,
|
2020-12-22 09:26:55 -08:00
|
|
|
&[],
|
2020-09-14 17:42:37 -07:00
|
|
|
&rent_collector,
|
|
|
|
None,
|
|
|
|
executors.clone(),
|
2020-09-24 07:36:22 -07:00
|
|
|
None,
|
2020-09-29 20:18:28 -07:00
|
|
|
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,
|
2020-09-14 17:42:37 -07:00
|
|
|
);
|
2019-06-10 19:50:02 -07:00
|
|
|
assert_eq!(
|
|
|
|
result,
|
|
|
|
Err(TransactionError::InstructionError(
|
|
|
|
0,
|
2019-11-05 08:38:35 -08:00
|
|
|
InstructionError::ReadonlyLamportChange
|
2019-06-10 19:50:02 -07:00
|
|
|
))
|
|
|
|
);
|
|
|
|
|
2020-06-24 13:52:38 -07:00
|
|
|
let message = Message::new(
|
2021-03-03 21:46:48 -08:00
|
|
|
&[Instruction::new_with_bincode(
|
2020-06-24 13:52:38 -07:00
|
|
|
mock_system_program_id,
|
|
|
|
&MockSystemInstruction::AttemptDataChange { data: 50 },
|
|
|
|
account_metas,
|
|
|
|
)],
|
|
|
|
Some(&from_pubkey),
|
|
|
|
);
|
2019-06-10 19:50:02 -07:00
|
|
|
|
2020-09-14 17:42:37 -07:00
|
|
|
let result = message_processor.process_message(
|
|
|
|
&message,
|
|
|
|
&loaders,
|
|
|
|
&accounts,
|
2020-12-22 09:26:55 -08:00
|
|
|
&[],
|
2020-09-14 17:42:37 -07:00
|
|
|
&rent_collector,
|
|
|
|
None,
|
|
|
|
executors,
|
2020-09-24 07:36:22 -07:00
|
|
|
None,
|
2020-09-29 20:18:28 -07:00
|
|
|
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,
|
2020-09-14 17:42:37 -07:00
|
|
|
);
|
2019-06-10 19:50:02 -07:00
|
|
|
assert_eq!(
|
|
|
|
result,
|
|
|
|
Err(TransactionError::InstructionError(
|
|
|
|
0,
|
2019-11-05 08:38:35 -08:00
|
|
|
InstructionError::ReadonlyDataModified
|
2019-06-10 19:50:02 -07:00
|
|
|
))
|
|
|
|
);
|
2019-02-22 12:08:54 -08:00
|
|
|
}
|
2020-01-22 09:11:56 -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],
|
2021-04-19 09:48:48 -07:00
|
|
|
invoke_context: &mut dyn InvokeContext,
|
2020-01-22 09:11:56 -08:00
|
|
|
) -> Result<(), InstructionError> {
|
2021-04-19 09:48:48 -07:00
|
|
|
let keyed_accounts = invoke_context.get_keyed_accounts()?;
|
2020-01-22 09:11:56 -08:00
|
|
|
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()?;
|
2021-04-22 13:04:55 -07:00
|
|
|
if from_account.lamports() != dup_account.lamports() {
|
2020-01-22 09:11:56 -08:00
|
|
|
return Err(InstructionError::InvalidArgument);
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
MockSystemInstruction::MultiBorrowMut => {
|
|
|
|
let from_lamports = {
|
|
|
|
let from_account = keyed_accounts[0].try_account_ref_mut()?;
|
2021-05-03 08:45:54 -07:00
|
|
|
from_account.lamports()
|
2020-01-22 09:11:56 -08:00
|
|
|
};
|
|
|
|
let dup_lamports = {
|
|
|
|
let dup_account = keyed_accounts[2].try_account_ref_mut()?;
|
2021-05-03 08:45:54 -07:00
|
|
|
dup_account.lamports()
|
2020-01-22 09:11:56 -08:00
|
|
|
};
|
|
|
|
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()?;
|
2021-05-03 08:45:15 -07:00
|
|
|
dup_account.checked_sub_lamports(lamports)?;
|
2021-04-27 07:56:18 -07:00
|
|
|
to_account.checked_add_lamports(lamports)?;
|
2021-03-11 14:40:45 -08:00
|
|
|
dup_account.set_data(vec![data]);
|
2020-01-22 09:11:56 -08:00
|
|
|
}
|
2021-05-03 08:45:15 -07:00
|
|
|
keyed_accounts[0]
|
|
|
|
.try_account_ref_mut()?
|
|
|
|
.checked_sub_lamports(lamports)?;
|
2021-04-27 07:56:18 -07:00
|
|
|
keyed_accounts[1]
|
|
|
|
.try_account_ref_mut()?
|
|
|
|
.checked_add_lamports(lamports)?;
|
2020-01-22 09:11:56 -08:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Err(InstructionError::InvalidInstructionData)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let mock_program_id = Pubkey::new(&[2u8; 32]);
|
2020-03-31 10:07:38 -07:00
|
|
|
let rent_collector = RentCollector::default();
|
2020-01-22 09:11:56 -08:00
|
|
|
let mut message_processor = MessageProcessor::default();
|
2020-05-19 19:45:30 -07:00
|
|
|
message_processor.add_program(mock_program_id, mock_system_process_instruction);
|
2020-01-22 09:11:56 -08:00
|
|
|
|
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);
|
2020-01-22 09:11:56 -08:00
|
|
|
accounts.push(account);
|
2021-03-09 13:06:07 -08:00
|
|
|
let account = AccountSharedData::new_ref(0, 1, &mock_program_id);
|
2020-01-22 09:11:56 -08:00
|
|
|
accounts.push(account);
|
|
|
|
|
2021-03-10 23:04:00 -08:00
|
|
|
let mut loaders: Vec<Vec<(Pubkey, Rc<RefCell<AccountSharedData>>)>> = Vec::new();
|
2021-03-24 23:23:20 -07:00
|
|
|
let account = Rc::new(RefCell::new(create_loadable_account_for_test(
|
2021-03-10 23:04:00 -08:00
|
|
|
"mock_system_program",
|
|
|
|
)));
|
2020-01-22 09:11:56 -08:00
|
|
|
loaders.push(vec![(mock_program_id, account)]);
|
|
|
|
|
2020-09-14 17:42:37 -07:00
|
|
|
let executors = Rc::new(RefCell::new(Executors::default()));
|
2021-04-12 16:04:57 -07:00
|
|
|
let ancestors = Ancestors::default();
|
2020-09-14 17:42:37 -07:00
|
|
|
|
2020-10-19 12:12:08 -07:00
|
|
|
let from_pubkey = solana_sdk::pubkey::new_rand();
|
|
|
|
let to_pubkey = solana_sdk::pubkey::new_rand();
|
2020-05-15 09:35:43 -07:00
|
|
|
let dup_pubkey = from_pubkey;
|
2020-01-22 09:11:56 -08:00
|
|
|
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
|
2020-06-24 13:52:38 -07:00
|
|
|
let message = Message::new(
|
2021-03-03 21:46:48 -08:00
|
|
|
&[Instruction::new_with_bincode(
|
2020-06-24 13:52:38 -07:00
|
|
|
mock_program_id,
|
|
|
|
&MockSystemInstruction::BorrowFail,
|
|
|
|
account_metas.clone(),
|
|
|
|
)],
|
|
|
|
Some(&from_pubkey),
|
|
|
|
);
|
2020-09-14 17:42:37 -07:00
|
|
|
let result = message_processor.process_message(
|
|
|
|
&message,
|
|
|
|
&loaders,
|
|
|
|
&accounts,
|
2020-12-22 09:26:55 -08:00
|
|
|
&[],
|
2020-09-14 17:42:37 -07:00
|
|
|
&rent_collector,
|
|
|
|
None,
|
|
|
|
executors.clone(),
|
2020-09-24 07:36:22 -07:00
|
|
|
None,
|
2020-09-29 20:18:28 -07:00
|
|
|
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,
|
2020-09-14 17:42:37 -07:00
|
|
|
);
|
2020-01-22 09:11:56 -08:00
|
|
|
assert_eq!(
|
|
|
|
result,
|
|
|
|
Err(TransactionError::InstructionError(
|
|
|
|
0,
|
|
|
|
InstructionError::AccountBorrowFailed
|
|
|
|
))
|
|
|
|
);
|
|
|
|
|
|
|
|
// Try to borrow mut the same account in a safe way
|
2020-06-24 13:52:38 -07:00
|
|
|
let message = Message::new(
|
2021-03-03 21:46:48 -08:00
|
|
|
&[Instruction::new_with_bincode(
|
2020-06-24 13:52:38 -07:00
|
|
|
mock_program_id,
|
|
|
|
&MockSystemInstruction::MultiBorrowMut,
|
|
|
|
account_metas.clone(),
|
|
|
|
)],
|
|
|
|
Some(&from_pubkey),
|
|
|
|
);
|
2020-09-14 17:42:37 -07:00
|
|
|
let result = message_processor.process_message(
|
|
|
|
&message,
|
|
|
|
&loaders,
|
|
|
|
&accounts,
|
2020-12-22 09:26:55 -08:00
|
|
|
&[],
|
2020-09-14 17:42:37 -07:00
|
|
|
&rent_collector,
|
|
|
|
None,
|
|
|
|
executors.clone(),
|
2020-09-24 07:36:22 -07:00
|
|
|
None,
|
2020-09-29 20:18:28 -07:00
|
|
|
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,
|
2020-09-14 17:42:37 -07:00
|
|
|
);
|
2020-01-22 09:11:56 -08:00
|
|
|
assert_eq!(result, Ok(()));
|
|
|
|
|
|
|
|
// Do work on the same account but at different location in keyed_accounts[]
|
2020-06-24 13:52:38 -07:00
|
|
|
let message = Message::new(
|
2021-03-03 21:46:48 -08:00
|
|
|
&[Instruction::new_with_bincode(
|
2020-06-24 13:52:38 -07:00
|
|
|
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();
|
2020-09-14 17:42:37 -07:00
|
|
|
let result = message_processor.process_message(
|
|
|
|
&message,
|
|
|
|
&loaders,
|
|
|
|
&accounts,
|
2020-12-22 09:26:55 -08:00
|
|
|
&[],
|
2020-09-14 17:42:37 -07:00
|
|
|
&rent_collector,
|
|
|
|
None,
|
|
|
|
executors,
|
2020-09-24 07:36:22 -07:00
|
|
|
None,
|
2020-09-29 20:18:28 -07:00
|
|
|
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,
|
2020-09-14 17:42:37 -07:00
|
|
|
);
|
2020-01-22 09:11:56 -08:00
|
|
|
assert_eq!(result, Ok(()));
|
2021-05-03 08:45:54 -07:00
|
|
|
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-01-22 09:11:56 -08:00
|
|
|
}
|
2020-04-28 14:33:56 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_process_cross_program() {
|
2021-01-22 15:28:01 -08:00
|
|
|
#[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],
|
2021-04-19 09:48:48 -07:00
|
|
|
invoke_context: &mut dyn InvokeContext,
|
2020-04-28 14:33:56 -07:00
|
|
|
) -> Result<(), InstructionError> {
|
2021-04-19 09:48:48 -07:00
|
|
|
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!(
|
2020-05-20 09:24:57 -07:00
|
|
|
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(())
|
|
|
|
}
|
|
|
|
|
2020-10-19 12:12:08 -07:00
|
|
|
let caller_program_id = solana_sdk::pubkey::new_rand();
|
|
|
|
let callee_program_id = solana_sdk::pubkey::new_rand();
|
2020-05-20 09:24:57 -07:00
|
|
|
|
2021-03-09 13:06:07 -08:00
|
|
|
let mut program_account = AccountSharedData::new(1, 0, &native_loader::id());
|
2021-04-28 07:07:43 -07:00
|
|
|
program_account.set_executable(true);
|
2021-03-10 23:04:00 -08:00
|
|
|
let executable_accounts = vec![(
|
|
|
|
callee_program_id,
|
|
|
|
Rc::new(RefCell::new(program_account.clone())),
|
|
|
|
)];
|
2020-04-28 14:33:56 -07:00
|
|
|
|
2020-10-19 12:12:08 -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
|
|
|
|
2020-10-19 12:12:08 -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
|
|
|
|
2020-07-10 13:02:55 -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)),
|
2020-11-29 02:06:43 -08:00
|
|
|
Rc::new(RefCell::new(program_account)),
|
2020-04-28 14:33:56 -07:00
|
|
|
];
|
2021-04-19 09:48:48 -07:00
|
|
|
|
|
|
|
let compiled_instruction = CompiledInstruction::new(2, &(), vec![0, 1, 2]);
|
2020-10-30 01:10:13 -07:00
|
|
|
let programs: Vec<(_, ProcessInstructionWithContext)> =
|
|
|
|
vec![(callee_program_id, mock_process_instruction)];
|
2021-04-19 09:48:48 -07:00
|
|
|
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(),
|
2021-04-19 09:48:48 -07:00
|
|
|
&message,
|
|
|
|
&compiled_instruction,
|
|
|
|
&executable_accounts,
|
|
|
|
&accounts,
|
2021-03-10 23:04:00 -08:00
|
|
|
&[],
|
2020-10-30 01:10:13 -07:00
|
|
|
programs.as_slice(),
|
2020-06-06 10:18:28 -07:00
|
|
|
None,
|
2020-10-28 13:16:13 -07:00
|
|
|
BpfComputeBudget::default(),
|
2020-09-14 17:42:37 -07:00
|
|
|
Rc::new(RefCell::new(Executors::default())),
|
2020-09-24 07:36:22 -07:00
|
|
|
None,
|
2020-09-29 20:18:28 -07:00
|
|
|
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)
|
2021-04-19 09:48:48 -07:00
|
|
|
let demote_sysvar_write_locks =
|
|
|
|
invoke_context.is_feature_active(&demote_sysvar_write_locks::id());
|
2021-03-10 23:04:00 -08:00
|
|
|
let caller_write_privileges = message
|
2021-01-22 15:28:01 -08:00
|
|
|
.account_keys
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
2021-03-30 10:05:09 -07:00
|
|
|
.map(|(i, _)| message.is_writable(i, demote_sysvar_write_locks))
|
2021-01-22 15:28:01 -08:00
|
|
|
.collect::<Vec<bool>>();
|
2021-04-19 09:48:48 -07:00
|
|
|
accounts[0].borrow_mut().data_as_mut_slice()[0] = 1;
|
2020-04-28 14:33:56 -07:00
|
|
|
assert_eq!(
|
2020-10-30 01:10:13 -07:00
|
|
|
MessageProcessor::process_cross_program_instruction(
|
2020-04-28 14:33:56 -07:00
|
|
|
&message,
|
|
|
|
&executable_accounts,
|
|
|
|
&accounts,
|
2021-03-10 23:04:00 -08:00
|
|
|
&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());
|
2020-06-24 13:52:38 -07:00
|
|
|
let message = Message::new(&[instruction], None);
|
2021-04-19 09:48:48 -07:00
|
|
|
|
|
|
|
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,
|
|
|
|
);
|
|
|
|
|
2021-03-10 23:04:00 -08:00
|
|
|
let caller_write_privileges = message
|
2021-01-22 15:28:01 -08:00
|
|
|
.account_keys
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
2021-03-30 10:05:09 -07:00
|
|
|
.map(|(i, _)| message.is_writable(i, demote_sysvar_write_locks))
|
2021-01-22 15:28:01 -08:00
|
|
|
.collect::<Vec<bool>>();
|
2020-04-28 14:33:56 -07:00
|
|
|
assert_eq!(
|
2020-10-30 01:10:13 -07:00
|
|
|
MessageProcessor::process_cross_program_instruction(
|
2020-04-28 14:33:56 -07:00
|
|
|
&message,
|
|
|
|
&executable_accounts,
|
|
|
|
&accounts,
|
2021-03-10 23:04:00 -08:00
|
|
|
&caller_write_privileges,
|
2020-04-28 14:33:56 -07:00
|
|
|
&mut invoke_context,
|
|
|
|
),
|
|
|
|
case.1
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2020-09-01 01:48:25 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_debug() {
|
|
|
|
let mut message_processor = MessageProcessor::default();
|
2020-12-13 17:26:34 -08:00
|
|
|
#[allow(clippy::unnecessary_wraps)]
|
2020-09-01 01:48:25 -07:00
|
|
|
fn mock_process_instruction(
|
|
|
|
_program_id: &Pubkey,
|
|
|
|
_data: &[u8],
|
2020-10-28 20:21:50 -07:00
|
|
|
_invoke_context: &mut dyn InvokeContext,
|
2020-09-01 01:48:25 -07:00
|
|
|
) -> Result<(), InstructionError> {
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-12-13 17:26:34 -08:00
|
|
|
#[allow(clippy::unnecessary_wraps)]
|
2020-09-01 01:48:25 -07:00
|
|
|
fn mock_ix_processor(
|
|
|
|
_pubkey: &Pubkey,
|
|
|
|
_data: &[u8],
|
2020-09-29 01:36:46 -07:00
|
|
|
_context: &mut dyn InvokeContext,
|
2020-10-20 09:05:45 -07:00
|
|
|
) -> Result<(), InstructionError> {
|
2020-09-01 01:48:25 -07:00
|
|
|
Ok(())
|
|
|
|
}
|
2020-10-19 12:12:08 -07:00
|
|
|
let program_id = solana_sdk::pubkey::new_rand();
|
2020-09-01 01:48:25 -07:00
|
|
|
message_processor.add_program(program_id, mock_process_instruction);
|
|
|
|
message_processor.add_loader(program_id, mock_ix_processor);
|
|
|
|
|
|
|
|
assert!(!format!("{:?}", message_processor).is_empty());
|
|
|
|
}
|
2019-02-18 22:26:22 -08:00
|
|
|
}
|