invoke_context.rs 65 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639
  1. use {
  2. crate::{
  3. execution_budget::{SVMTransactionExecutionBudget, SVMTransactionExecutionCost},
  4. loaded_programs::{
  5. ProgramCacheEntry, ProgramCacheEntryType, ProgramCacheForTxBatch,
  6. ProgramRuntimeEnvironments,
  7. },
  8. stable_log,
  9. sysvar_cache::SysvarCache,
  10. },
  11. solana_account::{create_account_shared_data_for_test, AccountSharedData},
  12. solana_epoch_schedule::EpochSchedule,
  13. solana_hash::Hash,
  14. solana_instruction::{error::InstructionError, AccountMeta, Instruction},
  15. solana_pubkey::Pubkey,
  16. solana_sbpf::{
  17. ebpf::MM_HEAP_START,
  18. elf::Executable as GenericExecutable,
  19. error::{EbpfError, ProgramResult},
  20. memory_region::MemoryMapping,
  21. program::{BuiltinFunction, SBPFVersion},
  22. vm::{Config, ContextObject, EbpfVm},
  23. },
  24. solana_sdk_ids::{
  25. bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable, loader_v4, native_loader, sysvar,
  26. },
  27. solana_svm_callback::InvokeContextCallback,
  28. solana_svm_feature_set::SVMFeatureSet,
  29. solana_svm_log_collector::{ic_msg, LogCollector},
  30. solana_svm_measure::measure::Measure,
  31. solana_svm_timings::{ExecuteDetailsTimings, ExecuteTimings},
  32. solana_svm_transaction::{instruction::SVMInstruction, svm_message::SVMMessage},
  33. solana_svm_type_overrides::sync::Arc,
  34. solana_transaction_context::{
  35. instruction::InstructionContext, instruction_accounts::InstructionAccount,
  36. transaction_accounts::KeyedAccountSharedData, IndexOfAccount, TransactionContext,
  37. MAX_ACCOUNTS_PER_TRANSACTION,
  38. },
  39. std::{
  40. alloc::Layout,
  41. borrow::Cow,
  42. cell::RefCell,
  43. fmt::{self, Debug},
  44. rc::Rc,
  45. },
  46. };
  47. pub type BuiltinFunctionWithContext = BuiltinFunction<InvokeContext<'static, 'static>>;
  48. pub type Executable = GenericExecutable<InvokeContext<'static, 'static>>;
  49. pub type RegisterTrace<'a> = &'a [[u64; 12]];
  50. /// Adapter so we can unify the interfaces of built-in programs and syscalls
  51. #[macro_export]
  52. macro_rules! declare_process_instruction {
  53. ($process_instruction:ident, $cu_to_consume:expr, |$invoke_context:ident| $inner:tt) => {
  54. $crate::solana_sbpf::declare_builtin_function!(
  55. $process_instruction,
  56. fn rust(
  57. invoke_context: &mut $crate::invoke_context::InvokeContext,
  58. _arg0: u64,
  59. _arg1: u64,
  60. _arg2: u64,
  61. _arg3: u64,
  62. _arg4: u64,
  63. _memory_mapping: &mut $crate::solana_sbpf::memory_region::MemoryMapping,
  64. ) -> std::result::Result<u64, Box<dyn std::error::Error>> {
  65. fn process_instruction_inner(
  66. $invoke_context: &mut $crate::invoke_context::InvokeContext,
  67. ) -> std::result::Result<(), $crate::__private::InstructionError>
  68. $inner
  69. let consumption_result = if $cu_to_consume > 0
  70. {
  71. invoke_context.consume_checked($cu_to_consume)
  72. } else {
  73. Ok(())
  74. };
  75. consumption_result
  76. .and_then(|_| {
  77. process_instruction_inner(invoke_context)
  78. .map(|_| 0)
  79. .map_err(|err| Box::new(err) as Box<dyn std::error::Error>)
  80. })
  81. .into()
  82. }
  83. );
  84. };
  85. }
  86. impl ContextObject for InvokeContext<'_, '_> {
  87. fn consume(&mut self, amount: u64) {
  88. // 1 to 1 instruction to compute unit mapping
  89. // ignore overflow, Ebpf will bail if exceeded
  90. let mut compute_meter = self.compute_meter.borrow_mut();
  91. *compute_meter = compute_meter.saturating_sub(amount);
  92. }
  93. fn get_remaining(&self) -> u64 {
  94. *self.compute_meter.borrow()
  95. }
  96. }
  97. #[derive(Clone, PartialEq, Eq, Debug)]
  98. pub struct AllocErr;
  99. impl fmt::Display for AllocErr {
  100. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  101. f.write_str("Error: Memory allocation failed")
  102. }
  103. }
  104. pub struct BpfAllocator {
  105. len: u64,
  106. pos: u64,
  107. }
  108. impl BpfAllocator {
  109. pub fn new(len: u64) -> Self {
  110. Self { len, pos: 0 }
  111. }
  112. pub fn alloc(&mut self, layout: Layout) -> Result<u64, AllocErr> {
  113. let bytes_to_align = (self.pos as *const u8).align_offset(layout.align()) as u64;
  114. if self
  115. .pos
  116. .saturating_add(bytes_to_align)
  117. .saturating_add(layout.size() as u64)
  118. <= self.len
  119. {
  120. self.pos = self.pos.saturating_add(bytes_to_align);
  121. let addr = MM_HEAP_START.saturating_add(self.pos);
  122. self.pos = self.pos.saturating_add(layout.size() as u64);
  123. Ok(addr)
  124. } else {
  125. Err(AllocErr)
  126. }
  127. }
  128. }
  129. pub struct EnvironmentConfig<'a> {
  130. pub blockhash: Hash,
  131. pub blockhash_lamports_per_signature: u64,
  132. epoch_stake_callback: &'a dyn InvokeContextCallback,
  133. feature_set: &'a SVMFeatureSet,
  134. pub program_runtime_environments_for_execution: &'a ProgramRuntimeEnvironments,
  135. pub program_runtime_environments_for_deployment: &'a ProgramRuntimeEnvironments,
  136. sysvar_cache: &'a SysvarCache,
  137. }
  138. impl<'a> EnvironmentConfig<'a> {
  139. pub fn new(
  140. blockhash: Hash,
  141. blockhash_lamports_per_signature: u64,
  142. epoch_stake_callback: &'a dyn InvokeContextCallback,
  143. feature_set: &'a SVMFeatureSet,
  144. program_runtime_environments_for_execution: &'a ProgramRuntimeEnvironments,
  145. program_runtime_environments_for_deployment: &'a ProgramRuntimeEnvironments,
  146. sysvar_cache: &'a SysvarCache,
  147. ) -> Self {
  148. Self {
  149. blockhash,
  150. blockhash_lamports_per_signature,
  151. epoch_stake_callback,
  152. feature_set,
  153. program_runtime_environments_for_execution,
  154. program_runtime_environments_for_deployment,
  155. sysvar_cache,
  156. }
  157. }
  158. }
  159. pub struct SyscallContext {
  160. pub allocator: BpfAllocator,
  161. pub accounts_metadata: Vec<SerializedAccountMetadata>,
  162. }
  163. #[derive(Debug, Clone)]
  164. pub struct SerializedAccountMetadata {
  165. pub original_data_len: usize,
  166. pub vm_data_addr: u64,
  167. pub vm_key_addr: u64,
  168. pub vm_lamports_addr: u64,
  169. pub vm_owner_addr: u64,
  170. }
  171. /// Main pipeline from runtime to program execution.
  172. pub struct InvokeContext<'a, 'ix_data> {
  173. /// Information about the currently executing transaction.
  174. pub transaction_context: &'a mut TransactionContext<'ix_data>,
  175. /// The local program cache for the transaction batch.
  176. pub program_cache_for_tx_batch: &'a mut ProgramCacheForTxBatch,
  177. /// Runtime configurations used to provision the invocation environment.
  178. pub environment_config: EnvironmentConfig<'a>,
  179. /// The compute budget for the current invocation.
  180. compute_budget: SVMTransactionExecutionBudget,
  181. /// The compute cost for the current invocation.
  182. execution_cost: SVMTransactionExecutionCost,
  183. /// Instruction compute meter, for tracking compute units consumed against
  184. /// the designated compute budget during program execution.
  185. compute_meter: RefCell<u64>,
  186. log_collector: Option<Rc<RefCell<LogCollector>>>,
  187. /// Latest measurement not yet accumulated in [ExecuteDetailsTimings::execute_us]
  188. pub execute_time: Option<Measure>,
  189. pub timings: ExecuteDetailsTimings,
  190. pub syscall_context: Vec<Option<SyscallContext>>,
  191. /// Pairs of index in TX instruction trace and VM register trace
  192. register_traces: Vec<(usize, Vec<[u64; 12]>)>,
  193. }
  194. impl<'a, 'ix_data> InvokeContext<'a, 'ix_data> {
  195. #[allow(clippy::too_many_arguments)]
  196. pub fn new(
  197. transaction_context: &'a mut TransactionContext<'ix_data>,
  198. program_cache_for_tx_batch: &'a mut ProgramCacheForTxBatch,
  199. environment_config: EnvironmentConfig<'a>,
  200. log_collector: Option<Rc<RefCell<LogCollector>>>,
  201. compute_budget: SVMTransactionExecutionBudget,
  202. execution_cost: SVMTransactionExecutionCost,
  203. ) -> Self {
  204. Self {
  205. transaction_context,
  206. program_cache_for_tx_batch,
  207. environment_config,
  208. log_collector,
  209. compute_budget,
  210. execution_cost,
  211. compute_meter: RefCell::new(compute_budget.compute_unit_limit),
  212. execute_time: None,
  213. timings: ExecuteDetailsTimings::default(),
  214. syscall_context: Vec::new(),
  215. register_traces: Vec::new(),
  216. }
  217. }
  218. /// Push a stack frame onto the invocation stack
  219. pub fn push(&mut self) -> Result<(), InstructionError> {
  220. let instruction_context = self
  221. .transaction_context
  222. .get_instruction_context_at_index_in_trace(
  223. self.transaction_context.get_instruction_trace_length(),
  224. )?;
  225. let program_id = instruction_context
  226. .get_program_key()
  227. .map_err(|_| InstructionError::UnsupportedProgramId)?;
  228. if self.transaction_context.get_instruction_stack_height() != 0 {
  229. let contains =
  230. (0..self.transaction_context.get_instruction_stack_height()).any(|level| {
  231. self.transaction_context
  232. .get_instruction_context_at_nesting_level(level)
  233. .and_then(|instruction_context| instruction_context.get_program_key())
  234. .map(|program_key| program_key == program_id)
  235. .unwrap_or(false)
  236. });
  237. let is_last = self
  238. .transaction_context
  239. .get_current_instruction_context()
  240. .and_then(|instruction_context| instruction_context.get_program_key())
  241. .map(|program_key| program_key == program_id)
  242. .unwrap_or(false);
  243. if contains && !is_last {
  244. // Reentrancy not allowed unless caller is calling itself
  245. return Err(InstructionError::ReentrancyNotAllowed);
  246. }
  247. }
  248. self.syscall_context.push(None);
  249. self.transaction_context.push()
  250. }
  251. /// Pop a stack frame from the invocation stack
  252. fn pop(&mut self) -> Result<(), InstructionError> {
  253. self.syscall_context.pop();
  254. self.transaction_context.pop()
  255. }
  256. /// Current height of the invocation stack, top level instructions are height
  257. /// `solana_instruction::TRANSACTION_LEVEL_STACK_HEIGHT`
  258. pub fn get_stack_height(&self) -> usize {
  259. self.transaction_context.get_instruction_stack_height()
  260. }
  261. /// Entrypoint for a cross-program invocation from a builtin program
  262. pub fn native_invoke(
  263. &mut self,
  264. instruction: Instruction,
  265. signers: &[Pubkey],
  266. ) -> Result<(), InstructionError> {
  267. self.prepare_next_instruction(instruction, signers)?;
  268. let mut compute_units_consumed = 0;
  269. self.process_instruction(&mut compute_units_consumed, &mut ExecuteTimings::default())?;
  270. Ok(())
  271. }
  272. /// Helper to prepare for process_instruction() when the instruction is not a top level one,
  273. /// and depends on `AccountMeta`s
  274. pub fn prepare_next_instruction(
  275. &mut self,
  276. instruction: Instruction,
  277. signers: &[Pubkey],
  278. ) -> Result<(), InstructionError> {
  279. // We reference accounts by an u8 index, so we have a total of 256 accounts.
  280. let mut transaction_callee_map: Vec<u16> = vec![u16::MAX; MAX_ACCOUNTS_PER_TRANSACTION];
  281. let mut instruction_accounts: Vec<InstructionAccount> =
  282. Vec::with_capacity(instruction.accounts.len());
  283. // This code block is necessary to restrict the scope of the immutable borrow of
  284. // transaction context (the `instruction_context` variable). At the end of this
  285. // function, we must borrow it again as mutable.
  286. let program_account_index = {
  287. let instruction_context = self.transaction_context.get_current_instruction_context()?;
  288. for account_meta in instruction.accounts.iter() {
  289. let index_in_transaction = self
  290. .transaction_context
  291. .find_index_of_account(&account_meta.pubkey)
  292. .ok_or_else(|| {
  293. ic_msg!(
  294. self,
  295. "Instruction references an unknown account {}",
  296. account_meta.pubkey,
  297. );
  298. InstructionError::MissingAccount
  299. })?;
  300. debug_assert!((index_in_transaction as usize) < transaction_callee_map.len());
  301. let index_in_callee = transaction_callee_map
  302. .get_mut(index_in_transaction as usize)
  303. .unwrap();
  304. if (*index_in_callee as usize) < instruction_accounts.len() {
  305. let cloned_account = {
  306. let instruction_account = instruction_accounts
  307. .get_mut(*index_in_callee as usize)
  308. .ok_or(InstructionError::MissingAccount)?;
  309. instruction_account.set_is_signer(
  310. instruction_account.is_signer() || account_meta.is_signer,
  311. );
  312. instruction_account.set_is_writable(
  313. instruction_account.is_writable() || account_meta.is_writable,
  314. );
  315. *instruction_account
  316. };
  317. instruction_accounts.push(cloned_account);
  318. } else {
  319. *index_in_callee = instruction_accounts.len() as u16;
  320. instruction_accounts.push(InstructionAccount::new(
  321. index_in_transaction,
  322. account_meta.is_signer,
  323. account_meta.is_writable,
  324. ));
  325. }
  326. }
  327. for current_index in 0..instruction_accounts.len() {
  328. let instruction_account = instruction_accounts.get(current_index).unwrap();
  329. let index_in_callee = *transaction_callee_map
  330. .get(instruction_account.index_in_transaction as usize)
  331. .unwrap() as usize;
  332. if current_index != index_in_callee {
  333. let (is_signer, is_writable) = {
  334. let reference_account = instruction_accounts
  335. .get(index_in_callee)
  336. .ok_or(InstructionError::MissingAccount)?;
  337. (
  338. reference_account.is_signer(),
  339. reference_account.is_writable(),
  340. )
  341. };
  342. let current_account = instruction_accounts.get_mut(current_index).unwrap();
  343. current_account.set_is_signer(current_account.is_signer() || is_signer);
  344. current_account.set_is_writable(current_account.is_writable() || is_writable);
  345. // This account is repeated, so there is no need to check for permissions
  346. continue;
  347. }
  348. let index_in_caller = instruction_context.get_index_of_account_in_instruction(
  349. instruction_account.index_in_transaction,
  350. )?;
  351. // This unwrap is safe because instruction.accounts.len() == instruction_accounts.len()
  352. let account_key = &instruction.accounts.get(current_index).unwrap().pubkey;
  353. // get_index_of_account_in_instruction has already checked if the index is valid.
  354. let caller_instruction_account = instruction_context
  355. .instruction_accounts()
  356. .get(index_in_caller as usize)
  357. .unwrap();
  358. // Readonly in caller cannot become writable in callee
  359. if instruction_account.is_writable() && !caller_instruction_account.is_writable() {
  360. ic_msg!(self, "{}'s writable privilege escalated", account_key,);
  361. return Err(InstructionError::PrivilegeEscalation);
  362. }
  363. // To be signed in the callee,
  364. // it must be either signed in the caller or by the program
  365. if instruction_account.is_signer()
  366. && !(caller_instruction_account.is_signer() || signers.contains(account_key))
  367. {
  368. ic_msg!(self, "{}'s signer privilege escalated", account_key,);
  369. return Err(InstructionError::PrivilegeEscalation);
  370. }
  371. }
  372. // Find and validate executables / program accounts
  373. let callee_program_id = &instruction.program_id;
  374. let program_account_index_in_transaction = self
  375. .transaction_context
  376. .find_index_of_account(callee_program_id);
  377. let program_account_index_in_instruction = program_account_index_in_transaction
  378. .map(|index| instruction_context.get_index_of_account_in_instruction(index));
  379. // We first check if the account exists in the transaction, and then see if it is part
  380. // of the instruction.
  381. if program_account_index_in_instruction.is_none()
  382. || program_account_index_in_instruction.unwrap().is_err()
  383. {
  384. ic_msg!(self, "Unknown program {}", callee_program_id);
  385. return Err(InstructionError::MissingAccount);
  386. }
  387. // SAFETY: This unwrap is safe, because we checked the index in instruction in the
  388. // previous if-condition.
  389. program_account_index_in_transaction.unwrap()
  390. };
  391. self.transaction_context.configure_next_instruction(
  392. program_account_index,
  393. instruction_accounts,
  394. transaction_callee_map,
  395. Cow::Owned(instruction.data),
  396. )?;
  397. Ok(())
  398. }
  399. /// Helper to prepare for process_instruction()/process_precompile() when the instruction is
  400. /// a top level one
  401. pub fn prepare_next_top_level_instruction(
  402. &mut self,
  403. message: &impl SVMMessage,
  404. instruction: &SVMInstruction,
  405. program_account_index: IndexOfAccount,
  406. data: &'ix_data [u8],
  407. ) -> Result<(), InstructionError> {
  408. // We reference accounts by an u8 index, so we have a total of 256 accounts.
  409. let mut transaction_callee_map: Vec<u16> = vec![u16::MAX; MAX_ACCOUNTS_PER_TRANSACTION];
  410. let mut instruction_accounts: Vec<InstructionAccount> =
  411. Vec::with_capacity(instruction.accounts.len());
  412. for index_in_transaction in instruction.accounts.iter() {
  413. debug_assert!((*index_in_transaction as usize) < transaction_callee_map.len());
  414. let index_in_callee = transaction_callee_map
  415. .get_mut(*index_in_transaction as usize)
  416. .unwrap();
  417. if (*index_in_callee as usize) > instruction_accounts.len() {
  418. *index_in_callee = instruction_accounts.len() as u16;
  419. }
  420. let index_in_transaction = *index_in_transaction as usize;
  421. instruction_accounts.push(InstructionAccount::new(
  422. index_in_transaction as IndexOfAccount,
  423. message.is_signer(index_in_transaction),
  424. message.is_writable(index_in_transaction),
  425. ));
  426. }
  427. self.transaction_context.configure_next_instruction(
  428. program_account_index,
  429. instruction_accounts,
  430. transaction_callee_map,
  431. Cow::Borrowed(data),
  432. )?;
  433. Ok(())
  434. }
  435. /// Processes an instruction and returns how many compute units were used
  436. pub fn process_instruction(
  437. &mut self,
  438. compute_units_consumed: &mut u64,
  439. timings: &mut ExecuteTimings,
  440. ) -> Result<(), InstructionError> {
  441. *compute_units_consumed = 0;
  442. self.push()?;
  443. self.process_executable_chain(compute_units_consumed, timings)
  444. // MUST pop if and only if `push` succeeded, independent of `result`.
  445. // Thus, the `.and()` instead of an `.and_then()`.
  446. .and(self.pop())
  447. }
  448. /// Processes a precompile instruction
  449. pub fn process_precompile(
  450. &mut self,
  451. program_id: &Pubkey,
  452. instruction_data: &[u8],
  453. message_instruction_datas_iter: impl Iterator<Item = &'ix_data [u8]>,
  454. ) -> Result<(), InstructionError> {
  455. self.push()?;
  456. let instruction_datas: Vec<_> = message_instruction_datas_iter.collect();
  457. self.environment_config
  458. .epoch_stake_callback
  459. .process_precompile(program_id, instruction_data, instruction_datas)
  460. .map_err(InstructionError::from)
  461. .and(self.pop())
  462. }
  463. /// Calls the instruction's program entrypoint method
  464. fn process_executable_chain(
  465. &mut self,
  466. compute_units_consumed: &mut u64,
  467. timings: &mut ExecuteTimings,
  468. ) -> Result<(), InstructionError> {
  469. let instruction_context = self.transaction_context.get_current_instruction_context()?;
  470. let process_executable_chain_time = Measure::start("process_executable_chain_time");
  471. let builtin_id = {
  472. let owner_id = instruction_context.get_program_owner()?;
  473. if native_loader::check_id(&owner_id) {
  474. *instruction_context.get_program_key()?
  475. } else if bpf_loader_deprecated::check_id(&owner_id)
  476. || bpf_loader::check_id(&owner_id)
  477. || bpf_loader_upgradeable::check_id(&owner_id)
  478. || loader_v4::check_id(&owner_id)
  479. {
  480. owner_id
  481. } else {
  482. return Err(InstructionError::UnsupportedProgramId);
  483. }
  484. };
  485. // The Murmur3 hash value (used by RBPF) of the string "entrypoint"
  486. const ENTRYPOINT_KEY: u32 = 0x71E3CF81;
  487. let entry = self
  488. .program_cache_for_tx_batch
  489. .find(&builtin_id)
  490. .ok_or(InstructionError::UnsupportedProgramId)?;
  491. let function = match &entry.program {
  492. ProgramCacheEntryType::Builtin(program) => program
  493. .get_function_registry()
  494. .lookup_by_key(ENTRYPOINT_KEY)
  495. .map(|(_name, function)| function),
  496. _ => None,
  497. }
  498. .ok_or(InstructionError::UnsupportedProgramId)?;
  499. let program_id = *instruction_context.get_program_key()?;
  500. self.transaction_context
  501. .set_return_data(program_id, Vec::new())?;
  502. let logger = self.get_log_collector();
  503. stable_log::program_invoke(&logger, &program_id, self.get_stack_height());
  504. let pre_remaining_units = self.get_remaining();
  505. // In program-runtime v2 we will create this VM instance only once per transaction.
  506. // `program_runtime_environment_v2.get_config()` will be used instead of `mock_config`.
  507. // For now, only built-ins are invoked from here, so the VM and its Config are irrelevant.
  508. let mock_config = Config::default();
  509. let empty_memory_mapping =
  510. MemoryMapping::new(Vec::new(), &mock_config, SBPFVersion::V0).unwrap();
  511. let mut vm = EbpfVm::new(
  512. self.environment_config
  513. .program_runtime_environments_for_execution
  514. .program_runtime_v2
  515. .clone(),
  516. SBPFVersion::V0,
  517. // Removes lifetime tracking
  518. unsafe { std::mem::transmute::<&mut InvokeContext, &mut InvokeContext>(self) },
  519. empty_memory_mapping,
  520. 0,
  521. );
  522. vm.invoke_function(function);
  523. let result = match vm.program_result {
  524. ProgramResult::Ok(_) => {
  525. stable_log::program_success(&logger, &program_id);
  526. Ok(())
  527. }
  528. ProgramResult::Err(ref err) => {
  529. if let EbpfError::SyscallError(syscall_error) = err {
  530. if let Some(instruction_err) = syscall_error.downcast_ref::<InstructionError>()
  531. {
  532. stable_log::program_failure(&logger, &program_id, instruction_err);
  533. Err(instruction_err.clone())
  534. } else {
  535. stable_log::program_failure(&logger, &program_id, syscall_error);
  536. Err(InstructionError::ProgramFailedToComplete)
  537. }
  538. } else {
  539. stable_log::program_failure(&logger, &program_id, err);
  540. Err(InstructionError::ProgramFailedToComplete)
  541. }
  542. }
  543. };
  544. let post_remaining_units = self.get_remaining();
  545. *compute_units_consumed = pre_remaining_units.saturating_sub(post_remaining_units);
  546. if builtin_id == program_id && result.is_ok() && *compute_units_consumed == 0 {
  547. return Err(InstructionError::BuiltinProgramsMustConsumeComputeUnits);
  548. }
  549. timings
  550. .execute_accessories
  551. .process_instructions
  552. .process_executable_chain_us += process_executable_chain_time.end_as_us();
  553. result
  554. }
  555. /// Get this invocation's LogCollector
  556. pub fn get_log_collector(&self) -> Option<Rc<RefCell<LogCollector>>> {
  557. self.log_collector.clone()
  558. }
  559. /// Consume compute units
  560. pub fn consume_checked(&self, amount: u64) -> Result<(), Box<dyn std::error::Error>> {
  561. let mut compute_meter = self.compute_meter.borrow_mut();
  562. let exceeded = *compute_meter < amount;
  563. *compute_meter = compute_meter.saturating_sub(amount);
  564. if exceeded {
  565. return Err(Box::new(InstructionError::ComputationalBudgetExceeded));
  566. }
  567. Ok(())
  568. }
  569. /// Set compute units
  570. ///
  571. /// Only use for tests and benchmarks
  572. pub fn mock_set_remaining(&self, remaining: u64) {
  573. *self.compute_meter.borrow_mut() = remaining;
  574. }
  575. /// Get this invocation's compute budget
  576. pub fn get_compute_budget(&self) -> &SVMTransactionExecutionBudget {
  577. &self.compute_budget
  578. }
  579. /// Get this invocation's compute budget
  580. pub fn get_execution_cost(&self) -> &SVMTransactionExecutionCost {
  581. &self.execution_cost
  582. }
  583. /// Get the current feature set.
  584. pub fn get_feature_set(&self) -> &SVMFeatureSet {
  585. self.environment_config.feature_set
  586. }
  587. pub fn get_program_runtime_environments_for_deployment(&self) -> &ProgramRuntimeEnvironments {
  588. self.environment_config
  589. .program_runtime_environments_for_deployment
  590. }
  591. pub fn is_stake_raise_minimum_delegation_to_1_sol_active(&self) -> bool {
  592. self.environment_config
  593. .feature_set
  594. .stake_raise_minimum_delegation_to_1_sol
  595. }
  596. pub fn is_deprecate_legacy_vote_ixs_active(&self) -> bool {
  597. self.environment_config
  598. .feature_set
  599. .deprecate_legacy_vote_ixs
  600. }
  601. /// Get cached sysvars
  602. pub fn get_sysvar_cache(&self) -> &SysvarCache {
  603. self.environment_config.sysvar_cache
  604. }
  605. /// Get cached epoch total stake.
  606. pub fn get_epoch_stake(&self) -> u64 {
  607. self.environment_config
  608. .epoch_stake_callback
  609. .get_epoch_stake()
  610. }
  611. /// Get cached stake for the epoch vote account.
  612. pub fn get_epoch_stake_for_vote_account(&self, pubkey: &'a Pubkey) -> u64 {
  613. self.environment_config
  614. .epoch_stake_callback
  615. .get_epoch_stake_for_vote_account(pubkey)
  616. }
  617. pub fn is_precompile(&self, pubkey: &Pubkey) -> bool {
  618. self.environment_config
  619. .epoch_stake_callback
  620. .is_precompile(pubkey)
  621. }
  622. // Should alignment be enforced during user pointer translation
  623. pub fn get_check_aligned(&self) -> bool {
  624. self.transaction_context
  625. .get_current_instruction_context()
  626. .and_then(|instruction_context| {
  627. let owner_id = instruction_context.get_program_owner();
  628. debug_assert!(owner_id.is_ok());
  629. owner_id
  630. })
  631. .map(|owner_key| owner_key != bpf_loader_deprecated::id())
  632. .unwrap_or(true)
  633. }
  634. // Set this instruction syscall context
  635. pub fn set_syscall_context(
  636. &mut self,
  637. syscall_context: SyscallContext,
  638. ) -> Result<(), InstructionError> {
  639. *self
  640. .syscall_context
  641. .last_mut()
  642. .ok_or(InstructionError::CallDepth)? = Some(syscall_context);
  643. Ok(())
  644. }
  645. // Get this instruction's SyscallContext
  646. pub fn get_syscall_context(&self) -> Result<&SyscallContext, InstructionError> {
  647. self.syscall_context
  648. .last()
  649. .and_then(std::option::Option::as_ref)
  650. .ok_or(InstructionError::CallDepth)
  651. }
  652. // Get this instruction's SyscallContext
  653. pub fn get_syscall_context_mut(&mut self) -> Result<&mut SyscallContext, InstructionError> {
  654. self.syscall_context
  655. .last_mut()
  656. .and_then(|syscall_context| syscall_context.as_mut())
  657. .ok_or(InstructionError::CallDepth)
  658. }
  659. /// Insert a VM register trace
  660. pub fn insert_register_trace(&mut self, register_trace: Vec<[u64; 12]>) {
  661. if register_trace.is_empty() {
  662. return;
  663. }
  664. let Ok(instruction_context) = self.transaction_context.get_current_instruction_context()
  665. else {
  666. return;
  667. };
  668. self.register_traces
  669. .push((instruction_context.get_index_in_trace(), register_trace));
  670. }
  671. /// Iterates over all VM register traces (including CPI)
  672. pub fn iterate_vm_traces(
  673. &self,
  674. callback: &dyn Fn(InstructionContext, &Executable, RegisterTrace),
  675. ) {
  676. for (index_in_trace, register_trace) in &self.register_traces {
  677. let Ok(instruction_context) = self
  678. .transaction_context
  679. .get_instruction_context_at_index_in_trace(*index_in_trace)
  680. else {
  681. continue;
  682. };
  683. let Ok(program_id) = instruction_context.get_program_key() else {
  684. continue;
  685. };
  686. let Some(entry) = self.program_cache_for_tx_batch.find(program_id) else {
  687. continue;
  688. };
  689. let ProgramCacheEntryType::Loaded(ref executable) = entry.program else {
  690. continue;
  691. };
  692. callback(instruction_context, executable, register_trace.as_slice());
  693. }
  694. }
  695. }
  696. #[macro_export]
  697. macro_rules! with_mock_invoke_context_with_feature_set {
  698. (
  699. $invoke_context:ident,
  700. $transaction_context:ident,
  701. $feature_set:ident,
  702. $transaction_accounts:expr $(,)?
  703. ) => {
  704. use {
  705. solana_svm_callback::InvokeContextCallback,
  706. solana_svm_log_collector::LogCollector,
  707. $crate::{
  708. __private::{Hash, ReadableAccount, Rent, TransactionContext},
  709. execution_budget::{SVMTransactionExecutionBudget, SVMTransactionExecutionCost},
  710. invoke_context::{EnvironmentConfig, InvokeContext},
  711. loaded_programs::{ProgramCacheForTxBatch, ProgramRuntimeEnvironments},
  712. sysvar_cache::SysvarCache,
  713. },
  714. };
  715. struct MockInvokeContextCallback {}
  716. impl InvokeContextCallback for MockInvokeContextCallback {}
  717. let compute_budget = SVMTransactionExecutionBudget::new_with_defaults(
  718. $feature_set.raise_cpi_nesting_limit_to_8,
  719. );
  720. let mut $transaction_context = TransactionContext::new(
  721. $transaction_accounts,
  722. Rent::default(),
  723. compute_budget.max_instruction_stack_depth,
  724. compute_budget.max_instruction_trace_length,
  725. );
  726. let mut sysvar_cache = SysvarCache::default();
  727. sysvar_cache.fill_missing_entries(|pubkey, callback| {
  728. for index in 0..$transaction_context.get_number_of_accounts() {
  729. if $transaction_context
  730. .get_key_of_account_at_index(index)
  731. .unwrap()
  732. == pubkey
  733. {
  734. callback(
  735. $transaction_context
  736. .accounts()
  737. .try_borrow(index)
  738. .unwrap()
  739. .data(),
  740. );
  741. }
  742. }
  743. });
  744. let program_runtime_environments = ProgramRuntimeEnvironments::default();
  745. let environment_config = EnvironmentConfig::new(
  746. Hash::default(),
  747. 0,
  748. &MockInvokeContextCallback {},
  749. $feature_set,
  750. &program_runtime_environments,
  751. &program_runtime_environments,
  752. &sysvar_cache,
  753. );
  754. let mut program_cache_for_tx_batch = ProgramCacheForTxBatch::default();
  755. let mut $invoke_context = InvokeContext::new(
  756. &mut $transaction_context,
  757. &mut program_cache_for_tx_batch,
  758. environment_config,
  759. Some(LogCollector::new_ref()),
  760. compute_budget,
  761. SVMTransactionExecutionCost::new_with_defaults(
  762. $feature_set.increase_cpi_account_info_limit,
  763. ),
  764. );
  765. };
  766. }
  767. #[macro_export]
  768. macro_rules! with_mock_invoke_context {
  769. (
  770. $invoke_context:ident,
  771. $transaction_context:ident,
  772. $transaction_accounts:expr $(,)?
  773. ) => {
  774. use $crate::with_mock_invoke_context_with_feature_set;
  775. let feature_set = &solana_svm_feature_set::SVMFeatureSet::default();
  776. with_mock_invoke_context_with_feature_set!(
  777. $invoke_context,
  778. $transaction_context,
  779. feature_set,
  780. $transaction_accounts
  781. )
  782. };
  783. }
  784. #[allow(clippy::too_many_arguments)]
  785. pub fn mock_process_instruction_with_feature_set<
  786. F: FnMut(&mut InvokeContext),
  787. G: FnMut(&mut InvokeContext),
  788. >(
  789. loader_id: &Pubkey,
  790. program_index: Option<IndexOfAccount>,
  791. instruction_data: &[u8],
  792. mut transaction_accounts: Vec<KeyedAccountSharedData>,
  793. instruction_account_metas: Vec<AccountMeta>,
  794. expected_result: Result<(), InstructionError>,
  795. builtin_function: BuiltinFunctionWithContext,
  796. mut pre_adjustments: F,
  797. mut post_adjustments: G,
  798. feature_set: &SVMFeatureSet,
  799. ) -> Vec<AccountSharedData> {
  800. let mut instruction_accounts: Vec<InstructionAccount> =
  801. Vec::with_capacity(instruction_account_metas.len());
  802. for account_meta in instruction_account_metas.iter() {
  803. let index_in_transaction = transaction_accounts
  804. .iter()
  805. .position(|(key, _account)| *key == account_meta.pubkey)
  806. .unwrap_or(transaction_accounts.len())
  807. as IndexOfAccount;
  808. instruction_accounts.push(InstructionAccount::new(
  809. index_in_transaction,
  810. account_meta.is_signer,
  811. account_meta.is_writable,
  812. ));
  813. }
  814. let program_index = if let Some(index) = program_index {
  815. index
  816. } else {
  817. let processor_account = AccountSharedData::new(0, 0, &native_loader::id());
  818. transaction_accounts.push((*loader_id, processor_account));
  819. transaction_accounts.len().saturating_sub(1) as IndexOfAccount
  820. };
  821. let pop_epoch_schedule_account = if !transaction_accounts
  822. .iter()
  823. .any(|(key, _)| *key == sysvar::epoch_schedule::id())
  824. {
  825. transaction_accounts.push((
  826. sysvar::epoch_schedule::id(),
  827. create_account_shared_data_for_test(&EpochSchedule::default()),
  828. ));
  829. true
  830. } else {
  831. false
  832. };
  833. with_mock_invoke_context_with_feature_set!(
  834. invoke_context,
  835. transaction_context,
  836. feature_set,
  837. transaction_accounts
  838. );
  839. let mut program_cache_for_tx_batch = ProgramCacheForTxBatch::default();
  840. program_cache_for_tx_batch.replenish(
  841. *loader_id,
  842. Arc::new(ProgramCacheEntry::new_builtin(0, 0, builtin_function)),
  843. );
  844. program_cache_for_tx_batch.set_slot_for_tests(
  845. invoke_context
  846. .get_sysvar_cache()
  847. .get_clock()
  848. .map(|clock| clock.slot)
  849. .unwrap_or(1),
  850. );
  851. invoke_context.program_cache_for_tx_batch = &mut program_cache_for_tx_batch;
  852. pre_adjustments(&mut invoke_context);
  853. invoke_context
  854. .transaction_context
  855. .configure_next_instruction_for_tests(
  856. program_index,
  857. instruction_accounts,
  858. instruction_data.to_vec(),
  859. )
  860. .unwrap();
  861. let result = invoke_context.process_instruction(&mut 0, &mut ExecuteTimings::default());
  862. assert_eq!(result, expected_result);
  863. post_adjustments(&mut invoke_context);
  864. let mut transaction_accounts = transaction_context.deconstruct_without_keys().unwrap();
  865. if pop_epoch_schedule_account {
  866. transaction_accounts.pop();
  867. }
  868. transaction_accounts.pop();
  869. transaction_accounts
  870. }
  871. pub fn mock_process_instruction<F: FnMut(&mut InvokeContext), G: FnMut(&mut InvokeContext)>(
  872. loader_id: &Pubkey,
  873. program_index: Option<IndexOfAccount>,
  874. instruction_data: &[u8],
  875. transaction_accounts: Vec<KeyedAccountSharedData>,
  876. instruction_account_metas: Vec<AccountMeta>,
  877. expected_result: Result<(), InstructionError>,
  878. builtin_function: BuiltinFunctionWithContext,
  879. pre_adjustments: F,
  880. post_adjustments: G,
  881. ) -> Vec<AccountSharedData> {
  882. mock_process_instruction_with_feature_set(
  883. loader_id,
  884. program_index,
  885. instruction_data,
  886. transaction_accounts,
  887. instruction_account_metas,
  888. expected_result,
  889. builtin_function,
  890. pre_adjustments,
  891. post_adjustments,
  892. &SVMFeatureSet::all_enabled(),
  893. )
  894. }
  895. #[cfg(test)]
  896. mod tests {
  897. use {
  898. super::*,
  899. crate::execution_budget::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT,
  900. serde::{Deserialize, Serialize},
  901. solana_account::WritableAccount,
  902. solana_instruction::Instruction,
  903. solana_keypair::Keypair,
  904. solana_rent::Rent,
  905. solana_signer::Signer,
  906. solana_transaction::{sanitized::SanitizedTransaction, Transaction},
  907. solana_transaction_context::MAX_ACCOUNTS_PER_INSTRUCTION,
  908. std::collections::HashSet,
  909. test_case::test_case,
  910. };
  911. #[derive(Debug, Serialize, Deserialize)]
  912. enum MockInstruction {
  913. NoopSuccess,
  914. NoopFail,
  915. ModifyOwned,
  916. ModifyNotOwned,
  917. ModifyReadonly,
  918. UnbalancedPush,
  919. UnbalancedPop,
  920. ConsumeComputeUnits {
  921. compute_units_to_consume: u64,
  922. desired_result: Result<(), InstructionError>,
  923. },
  924. Resize {
  925. new_len: u64,
  926. },
  927. }
  928. const MOCK_BUILTIN_COMPUTE_UNIT_COST: u64 = 1;
  929. declare_process_instruction!(
  930. MockBuiltin,
  931. MOCK_BUILTIN_COMPUTE_UNIT_COST,
  932. |invoke_context| {
  933. let transaction_context = &invoke_context.transaction_context;
  934. let instruction_context = transaction_context.get_current_instruction_context()?;
  935. let instruction_data = instruction_context.get_instruction_data();
  936. let program_id = instruction_context.get_program_key()?;
  937. let instruction_accounts = (0..4)
  938. .map(|instruction_account_index| {
  939. InstructionAccount::new(instruction_account_index, false, false)
  940. })
  941. .collect::<Vec<_>>();
  942. assert_eq!(
  943. program_id,
  944. instruction_context
  945. .try_borrow_instruction_account(0)?
  946. .get_owner()
  947. );
  948. assert_ne!(
  949. instruction_context
  950. .try_borrow_instruction_account(1)?
  951. .get_owner(),
  952. instruction_context.get_key_of_instruction_account(0)?
  953. );
  954. if let Ok(instruction) = bincode::deserialize(instruction_data) {
  955. match instruction {
  956. MockInstruction::NoopSuccess => (),
  957. MockInstruction::NoopFail => return Err(InstructionError::GenericError),
  958. MockInstruction::ModifyOwned => instruction_context
  959. .try_borrow_instruction_account(0)?
  960. .set_data_from_slice(&[1])?,
  961. MockInstruction::ModifyNotOwned => instruction_context
  962. .try_borrow_instruction_account(1)?
  963. .set_data_from_slice(&[1])?,
  964. MockInstruction::ModifyReadonly => instruction_context
  965. .try_borrow_instruction_account(2)?
  966. .set_data_from_slice(&[1])?,
  967. MockInstruction::UnbalancedPush => {
  968. instruction_context
  969. .try_borrow_instruction_account(0)?
  970. .checked_add_lamports(1)?;
  971. let program_id = *transaction_context.get_key_of_account_at_index(3)?;
  972. let metas = vec![
  973. AccountMeta::new_readonly(
  974. *transaction_context.get_key_of_account_at_index(0)?,
  975. false,
  976. ),
  977. AccountMeta::new_readonly(
  978. *transaction_context.get_key_of_account_at_index(1)?,
  979. false,
  980. ),
  981. ];
  982. let inner_instruction = Instruction::new_with_bincode(
  983. program_id,
  984. &MockInstruction::NoopSuccess,
  985. metas,
  986. );
  987. invoke_context
  988. .transaction_context
  989. .configure_next_instruction_for_tests(3, instruction_accounts, vec![])
  990. .unwrap();
  991. let result = invoke_context.push();
  992. assert_eq!(result, Err(InstructionError::UnbalancedInstruction));
  993. result?;
  994. invoke_context
  995. .native_invoke(inner_instruction, &[])
  996. .and(invoke_context.pop())?;
  997. }
  998. MockInstruction::UnbalancedPop => instruction_context
  999. .try_borrow_instruction_account(0)?
  1000. .checked_add_lamports(1)?,
  1001. MockInstruction::ConsumeComputeUnits {
  1002. compute_units_to_consume,
  1003. desired_result,
  1004. } => {
  1005. invoke_context
  1006. .consume_checked(compute_units_to_consume)
  1007. .map_err(|_| InstructionError::ComputationalBudgetExceeded)?;
  1008. return desired_result;
  1009. }
  1010. MockInstruction::Resize { new_len } => instruction_context
  1011. .try_borrow_instruction_account(0)?
  1012. .set_data_from_slice(&vec![0; new_len as usize])?,
  1013. }
  1014. } else {
  1015. return Err(InstructionError::InvalidInstructionData);
  1016. }
  1017. Ok(())
  1018. }
  1019. );
  1020. #[test_case(false; "SIMD-0268 disabled")]
  1021. #[test_case(true; "SIMD-0268 enabled")]
  1022. fn test_instruction_stack_height(simd_0268_active: bool) {
  1023. let one_more_than_max_depth =
  1024. SVMTransactionExecutionBudget::new_with_defaults(simd_0268_active)
  1025. .max_instruction_stack_depth
  1026. .saturating_add(1);
  1027. let mut invoke_stack = vec![];
  1028. let mut transaction_accounts = vec![];
  1029. let mut instruction_accounts = vec![];
  1030. for index in 0..one_more_than_max_depth {
  1031. invoke_stack.push(solana_pubkey::new_rand());
  1032. transaction_accounts.push((
  1033. solana_pubkey::new_rand(),
  1034. AccountSharedData::new(index as u64, 1, invoke_stack.get(index).unwrap()),
  1035. ));
  1036. instruction_accounts.push(InstructionAccount::new(
  1037. index as IndexOfAccount,
  1038. false,
  1039. true,
  1040. ));
  1041. }
  1042. for (index, program_id) in invoke_stack.iter().enumerate() {
  1043. transaction_accounts.push((
  1044. *program_id,
  1045. AccountSharedData::new(1, 1, &solana_pubkey::Pubkey::default()),
  1046. ));
  1047. instruction_accounts.push(InstructionAccount::new(
  1048. index as IndexOfAccount,
  1049. false,
  1050. false,
  1051. ));
  1052. }
  1053. with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
  1054. // Check call depth increases and has a limit
  1055. let mut depth_reached: usize = 0;
  1056. for _ in 0..invoke_stack.len() {
  1057. invoke_context
  1058. .transaction_context
  1059. .configure_next_instruction_for_tests(
  1060. one_more_than_max_depth.saturating_add(depth_reached) as IndexOfAccount,
  1061. instruction_accounts.clone(),
  1062. vec![],
  1063. )
  1064. .unwrap();
  1065. if Err(InstructionError::CallDepth) == invoke_context.push() {
  1066. break;
  1067. }
  1068. depth_reached = depth_reached.saturating_add(1);
  1069. }
  1070. assert_ne!(depth_reached, 0);
  1071. assert!(depth_reached < one_more_than_max_depth);
  1072. }
  1073. #[test]
  1074. fn test_max_instruction_trace_length() {
  1075. const MAX_INSTRUCTIONS: usize = 8;
  1076. let mut transaction_context = TransactionContext::new(
  1077. vec![(
  1078. Pubkey::new_unique(),
  1079. AccountSharedData::new(1, 1, &Pubkey::new_unique()),
  1080. )],
  1081. Rent::default(),
  1082. 1,
  1083. MAX_INSTRUCTIONS,
  1084. );
  1085. for _ in 0..MAX_INSTRUCTIONS {
  1086. transaction_context.push().unwrap();
  1087. transaction_context
  1088. .configure_next_instruction_for_tests(
  1089. 0,
  1090. vec![InstructionAccount::new(0, false, false)],
  1091. vec![],
  1092. )
  1093. .unwrap();
  1094. transaction_context.pop().unwrap();
  1095. }
  1096. assert_eq!(
  1097. transaction_context.push(),
  1098. Err(InstructionError::MaxInstructionTraceLengthExceeded)
  1099. );
  1100. }
  1101. #[test_case(MockInstruction::NoopSuccess, Ok(()); "NoopSuccess")]
  1102. #[test_case(MockInstruction::NoopFail, Err(InstructionError::GenericError); "NoopFail")]
  1103. #[test_case(MockInstruction::ModifyOwned, Ok(()); "ModifyOwned")]
  1104. #[test_case(MockInstruction::ModifyNotOwned, Err(InstructionError::ExternalAccountDataModified); "ModifyNotOwned")]
  1105. #[test_case(MockInstruction::ModifyReadonly, Err(InstructionError::ReadonlyDataModified); "ModifyReadonly")]
  1106. #[test_case(MockInstruction::UnbalancedPush, Err(InstructionError::UnbalancedInstruction); "UnbalancedPush")]
  1107. #[test_case(MockInstruction::UnbalancedPop, Err(InstructionError::UnbalancedInstruction); "UnbalancedPop")]
  1108. fn test_process_instruction_account_modifications(
  1109. instruction: MockInstruction,
  1110. expected_result: Result<(), InstructionError>,
  1111. ) {
  1112. let callee_program_id = solana_pubkey::new_rand();
  1113. let owned_account = AccountSharedData::new(42, 1, &callee_program_id);
  1114. let not_owned_account = AccountSharedData::new(84, 1, &solana_pubkey::new_rand());
  1115. let readonly_account = AccountSharedData::new(168, 1, &solana_pubkey::new_rand());
  1116. let loader_account = AccountSharedData::new(0, 1, &native_loader::id());
  1117. let mut program_account = AccountSharedData::new(1, 1, &native_loader::id());
  1118. program_account.set_executable(true);
  1119. let transaction_accounts = vec![
  1120. (solana_pubkey::new_rand(), owned_account),
  1121. (solana_pubkey::new_rand(), not_owned_account),
  1122. (solana_pubkey::new_rand(), readonly_account),
  1123. (callee_program_id, program_account),
  1124. (solana_pubkey::new_rand(), loader_account),
  1125. ];
  1126. let metas = vec![
  1127. AccountMeta::new(transaction_accounts.first().unwrap().0, false),
  1128. AccountMeta::new(transaction_accounts.get(1).unwrap().0, false),
  1129. AccountMeta::new_readonly(transaction_accounts.get(2).unwrap().0, false),
  1130. ];
  1131. let instruction_accounts = (0..4)
  1132. .map(|instruction_account_index| {
  1133. InstructionAccount::new(
  1134. instruction_account_index,
  1135. false,
  1136. instruction_account_index < 2,
  1137. )
  1138. })
  1139. .collect::<Vec<_>>();
  1140. with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
  1141. let mut program_cache_for_tx_batch = ProgramCacheForTxBatch::default();
  1142. program_cache_for_tx_batch.replenish(
  1143. callee_program_id,
  1144. Arc::new(ProgramCacheEntry::new_builtin(0, 1, MockBuiltin::vm)),
  1145. );
  1146. invoke_context.program_cache_for_tx_batch = &mut program_cache_for_tx_batch;
  1147. // Account modification tests
  1148. invoke_context
  1149. .transaction_context
  1150. .configure_next_instruction_for_tests(4, instruction_accounts, vec![])
  1151. .unwrap();
  1152. invoke_context.push().unwrap();
  1153. let inner_instruction =
  1154. Instruction::new_with_bincode(callee_program_id, &instruction, metas.clone());
  1155. let result = invoke_context
  1156. .native_invoke(inner_instruction, &[])
  1157. .and(invoke_context.pop());
  1158. assert_eq!(result, expected_result);
  1159. }
  1160. #[test_case(Ok(()); "Ok")]
  1161. #[test_case(Err(InstructionError::GenericError); "GenericError")]
  1162. fn test_process_instruction_compute_unit_consumption(
  1163. expected_result: Result<(), InstructionError>,
  1164. ) {
  1165. let callee_program_id = solana_pubkey::new_rand();
  1166. let owned_account = AccountSharedData::new(42, 1, &callee_program_id);
  1167. let not_owned_account = AccountSharedData::new(84, 1, &solana_pubkey::new_rand());
  1168. let readonly_account = AccountSharedData::new(168, 1, &solana_pubkey::new_rand());
  1169. let loader_account = AccountSharedData::new(0, 1, &native_loader::id());
  1170. let mut program_account = AccountSharedData::new(1, 1, &native_loader::id());
  1171. program_account.set_executable(true);
  1172. let transaction_accounts = vec![
  1173. (solana_pubkey::new_rand(), owned_account),
  1174. (solana_pubkey::new_rand(), not_owned_account),
  1175. (solana_pubkey::new_rand(), readonly_account),
  1176. (callee_program_id, program_account),
  1177. (solana_pubkey::new_rand(), loader_account),
  1178. ];
  1179. let metas = vec![
  1180. AccountMeta::new(transaction_accounts.first().unwrap().0, false),
  1181. AccountMeta::new(transaction_accounts.get(1).unwrap().0, false),
  1182. AccountMeta::new_readonly(transaction_accounts.get(2).unwrap().0, false),
  1183. ];
  1184. let instruction_accounts = (0..4)
  1185. .map(|instruction_account_index| {
  1186. InstructionAccount::new(
  1187. instruction_account_index,
  1188. false,
  1189. instruction_account_index < 2,
  1190. )
  1191. })
  1192. .collect::<Vec<_>>();
  1193. with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
  1194. let mut program_cache_for_tx_batch = ProgramCacheForTxBatch::default();
  1195. program_cache_for_tx_batch.replenish(
  1196. callee_program_id,
  1197. Arc::new(ProgramCacheEntry::new_builtin(0, 1, MockBuiltin::vm)),
  1198. );
  1199. invoke_context.program_cache_for_tx_batch = &mut program_cache_for_tx_batch;
  1200. // Compute unit consumption tests
  1201. let compute_units_to_consume = 10;
  1202. invoke_context
  1203. .transaction_context
  1204. .configure_next_instruction_for_tests(4, instruction_accounts, vec![])
  1205. .unwrap();
  1206. invoke_context.push().unwrap();
  1207. let inner_instruction = Instruction::new_with_bincode(
  1208. callee_program_id,
  1209. &MockInstruction::ConsumeComputeUnits {
  1210. compute_units_to_consume,
  1211. desired_result: expected_result.clone(),
  1212. },
  1213. metas.clone(),
  1214. );
  1215. invoke_context
  1216. .prepare_next_instruction(inner_instruction, &[])
  1217. .unwrap();
  1218. let mut compute_units_consumed = 0;
  1219. let result = invoke_context
  1220. .process_instruction(&mut compute_units_consumed, &mut ExecuteTimings::default());
  1221. // Because the instruction had compute cost > 0, then regardless of the execution result,
  1222. // the number of compute units consumed should be a non-default which is something greater
  1223. // than zero.
  1224. assert!(compute_units_consumed > 0);
  1225. assert_eq!(
  1226. compute_units_consumed,
  1227. compute_units_to_consume.saturating_add(MOCK_BUILTIN_COMPUTE_UNIT_COST),
  1228. );
  1229. assert_eq!(result, expected_result);
  1230. invoke_context.pop().unwrap();
  1231. }
  1232. #[test]
  1233. fn test_invoke_context_compute_budget() {
  1234. let transaction_accounts = vec![(solana_pubkey::new_rand(), AccountSharedData::default())];
  1235. let execution_budget = SVMTransactionExecutionBudget {
  1236. compute_unit_limit: u64::from(DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT),
  1237. ..SVMTransactionExecutionBudget::default()
  1238. };
  1239. with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
  1240. invoke_context.compute_budget = execution_budget;
  1241. invoke_context
  1242. .transaction_context
  1243. .configure_next_instruction_for_tests(0, vec![], vec![])
  1244. .unwrap();
  1245. invoke_context.push().unwrap();
  1246. assert_eq!(*invoke_context.get_compute_budget(), execution_budget);
  1247. invoke_context.pop().unwrap();
  1248. }
  1249. #[test_case(0; "Resize the account to *the same size*, so not consuming any additional size")]
  1250. #[test_case(1; "Resize the account larger")]
  1251. #[test_case(-1; "Resize the account smaller")]
  1252. fn test_process_instruction_accounts_resize_delta(resize_delta: i64) {
  1253. let program_key = Pubkey::new_unique();
  1254. let user_account_data_len = 123u64;
  1255. let user_account =
  1256. AccountSharedData::new(100, user_account_data_len as usize, &program_key);
  1257. let dummy_account = AccountSharedData::new(10, 0, &program_key);
  1258. let mut program_account = AccountSharedData::new(500, 500, &native_loader::id());
  1259. program_account.set_executable(true);
  1260. let transaction_accounts = vec![
  1261. (Pubkey::new_unique(), user_account),
  1262. (Pubkey::new_unique(), dummy_account),
  1263. (program_key, program_account),
  1264. ];
  1265. let instruction_accounts = vec![
  1266. InstructionAccount::new(0, false, true),
  1267. InstructionAccount::new(1, false, false),
  1268. ];
  1269. with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
  1270. let mut program_cache_for_tx_batch = ProgramCacheForTxBatch::default();
  1271. program_cache_for_tx_batch.replenish(
  1272. program_key,
  1273. Arc::new(ProgramCacheEntry::new_builtin(0, 0, MockBuiltin::vm)),
  1274. );
  1275. invoke_context.program_cache_for_tx_batch = &mut program_cache_for_tx_batch;
  1276. let new_len = (user_account_data_len as i64).saturating_add(resize_delta) as u64;
  1277. let instruction_data = bincode::serialize(&MockInstruction::Resize { new_len }).unwrap();
  1278. invoke_context
  1279. .transaction_context
  1280. .configure_next_instruction_for_tests(2, instruction_accounts, instruction_data)
  1281. .unwrap();
  1282. let result = invoke_context.process_instruction(&mut 0, &mut ExecuteTimings::default());
  1283. assert!(result.is_ok());
  1284. assert_eq!(
  1285. invoke_context.transaction_context.accounts().resize_delta(),
  1286. resize_delta
  1287. );
  1288. }
  1289. #[test]
  1290. fn test_prepare_instruction_maximum_accounts() {
  1291. const MAX_ACCOUNTS_REFERENCED: usize = u16::MAX as usize;
  1292. let mut transaction_accounts: Vec<KeyedAccountSharedData> =
  1293. Vec::with_capacity(MAX_ACCOUNTS_PER_TRANSACTION);
  1294. let mut account_metas: Vec<AccountMeta> = Vec::with_capacity(MAX_ACCOUNTS_REFERENCED);
  1295. // Fee-payer
  1296. let fee_payer = Keypair::new();
  1297. transaction_accounts.push((
  1298. fee_payer.pubkey(),
  1299. AccountSharedData::new(1, 1, &Pubkey::new_unique()),
  1300. ));
  1301. account_metas.push(AccountMeta::new(fee_payer.pubkey(), true));
  1302. let program_id = Pubkey::new_unique();
  1303. let mut program_account = AccountSharedData::new(1, 1, &Pubkey::new_unique());
  1304. program_account.set_executable(true);
  1305. transaction_accounts.push((program_id, program_account));
  1306. account_metas.push(AccountMeta::new_readonly(program_id, false));
  1307. for i in 2..MAX_ACCOUNTS_REFERENCED {
  1308. // Let's reference 256 unique accounts, and the rest is repeated.
  1309. if i < MAX_ACCOUNTS_PER_TRANSACTION {
  1310. let key = Pubkey::new_unique();
  1311. transaction_accounts
  1312. .push((key, AccountSharedData::new(1, 1, &Pubkey::new_unique())));
  1313. account_metas.push(AccountMeta::new_readonly(key, false));
  1314. } else {
  1315. let repeated_key = transaction_accounts
  1316. .get(i % MAX_ACCOUNTS_PER_TRANSACTION)
  1317. .unwrap()
  1318. .0;
  1319. account_metas.push(AccountMeta::new_readonly(repeated_key, false));
  1320. }
  1321. }
  1322. with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
  1323. let instruction_1 = Instruction::new_with_bytes(program_id, &[20], account_metas.clone());
  1324. let instruction_2 = Instruction::new_with_bytes(
  1325. program_id,
  1326. &[20],
  1327. account_metas.iter().rev().cloned().collect(),
  1328. );
  1329. let transaction = Transaction::new_with_payer(
  1330. &[instruction_1.clone(), instruction_2.clone()],
  1331. Some(&fee_payer.pubkey()),
  1332. );
  1333. let sanitized =
  1334. SanitizedTransaction::try_from_legacy_transaction(transaction, &HashSet::new())
  1335. .unwrap();
  1336. fn test_case_1(invoke_context: &InvokeContext) {
  1337. let instruction_context = invoke_context
  1338. .transaction_context
  1339. .get_next_instruction_context()
  1340. .unwrap();
  1341. for index_in_instruction in 0..MAX_ACCOUNTS_REFERENCED as IndexOfAccount {
  1342. let index_in_transaction = instruction_context
  1343. .get_index_of_instruction_account_in_transaction(index_in_instruction)
  1344. .unwrap();
  1345. let other_ix_index = instruction_context
  1346. .get_index_of_account_in_instruction(index_in_transaction)
  1347. .unwrap();
  1348. if (index_in_instruction as usize) < MAX_ACCOUNTS_PER_TRANSACTION {
  1349. assert_eq!(index_in_instruction, index_in_transaction);
  1350. assert_eq!(index_in_instruction, other_ix_index);
  1351. } else {
  1352. assert_eq!(
  1353. index_in_instruction as usize % MAX_ACCOUNTS_PER_TRANSACTION,
  1354. index_in_transaction as usize
  1355. );
  1356. assert_eq!(
  1357. index_in_instruction as usize % MAX_ACCOUNTS_PER_TRANSACTION,
  1358. other_ix_index as usize
  1359. );
  1360. }
  1361. }
  1362. }
  1363. fn test_case_2(invoke_context: &InvokeContext) {
  1364. let instruction_context = invoke_context
  1365. .transaction_context
  1366. .get_next_instruction_context()
  1367. .unwrap();
  1368. for index_in_instruction in 0..MAX_ACCOUNTS_REFERENCED as IndexOfAccount {
  1369. let index_in_transaction = instruction_context
  1370. .get_index_of_instruction_account_in_transaction(index_in_instruction)
  1371. .unwrap();
  1372. let other_ix_index = instruction_context
  1373. .get_index_of_account_in_instruction(index_in_transaction)
  1374. .unwrap();
  1375. assert_eq!(
  1376. index_in_transaction,
  1377. (MAX_ACCOUNTS_REFERENCED as u16)
  1378. .saturating_sub(index_in_instruction)
  1379. .saturating_sub(1)
  1380. .overflowing_rem(MAX_ACCOUNTS_PER_TRANSACTION as u16)
  1381. .0
  1382. );
  1383. if (index_in_instruction as usize) < MAX_ACCOUNTS_PER_TRANSACTION {
  1384. assert_eq!(index_in_instruction, other_ix_index);
  1385. } else {
  1386. assert_eq!(
  1387. index_in_instruction as usize % MAX_ACCOUNTS_PER_TRANSACTION,
  1388. other_ix_index as usize
  1389. );
  1390. }
  1391. }
  1392. }
  1393. let svm_instruction =
  1394. SVMInstruction::from(sanitized.message().instructions().first().unwrap());
  1395. invoke_context
  1396. .prepare_next_top_level_instruction(
  1397. &sanitized,
  1398. &svm_instruction,
  1399. 90,
  1400. svm_instruction.data,
  1401. )
  1402. .unwrap();
  1403. test_case_1(&invoke_context);
  1404. invoke_context.transaction_context.push().unwrap();
  1405. let svm_instruction =
  1406. SVMInstruction::from(sanitized.message().instructions().get(1).unwrap());
  1407. invoke_context
  1408. .prepare_next_top_level_instruction(
  1409. &sanitized,
  1410. &svm_instruction,
  1411. 90,
  1412. svm_instruction.data,
  1413. )
  1414. .unwrap();
  1415. test_case_2(&invoke_context);
  1416. invoke_context.transaction_context.push().unwrap();
  1417. invoke_context
  1418. .prepare_next_instruction(instruction_1, &[fee_payer.pubkey()])
  1419. .unwrap();
  1420. test_case_1(&invoke_context);
  1421. invoke_context.transaction_context.push().unwrap();
  1422. invoke_context
  1423. .prepare_next_instruction(instruction_2, &[fee_payer.pubkey()])
  1424. .unwrap();
  1425. test_case_2(&invoke_context);
  1426. }
  1427. #[test]
  1428. fn test_duplicated_accounts() {
  1429. let mut transaction_accounts: Vec<KeyedAccountSharedData> =
  1430. Vec::with_capacity(MAX_ACCOUNTS_PER_TRANSACTION);
  1431. let mut account_metas: Vec<AccountMeta> =
  1432. Vec::with_capacity(MAX_ACCOUNTS_PER_INSTRUCTION.saturating_sub(1));
  1433. // Fee-payer
  1434. let fee_payer = Keypair::new();
  1435. transaction_accounts.push((
  1436. fee_payer.pubkey(),
  1437. AccountSharedData::new(1, 1, &Pubkey::new_unique()),
  1438. ));
  1439. account_metas.push(AccountMeta::new(fee_payer.pubkey(), true));
  1440. let program_id = Pubkey::new_unique();
  1441. let mut program_account = AccountSharedData::new(1, 1, &Pubkey::new_unique());
  1442. program_account.set_executable(true);
  1443. transaction_accounts.push((program_id, program_account));
  1444. account_metas.push(AccountMeta::new_readonly(program_id, false));
  1445. for i in 2..account_metas.capacity() {
  1446. if i % 2 == 0 {
  1447. let key = Pubkey::new_unique();
  1448. transaction_accounts
  1449. .push((key, AccountSharedData::new(1, 1, &Pubkey::new_unique())));
  1450. account_metas.push(AccountMeta::new_readonly(key, false));
  1451. } else {
  1452. let last_key = transaction_accounts.last().unwrap().0;
  1453. account_metas.push(AccountMeta::new_readonly(last_key, false));
  1454. }
  1455. }
  1456. with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
  1457. let instruction = Instruction::new_with_bytes(program_id, &[20], account_metas.clone());
  1458. let transaction = Transaction::new_with_payer(&[instruction], Some(&fee_payer.pubkey()));
  1459. let sanitized =
  1460. SanitizedTransaction::try_from_legacy_transaction(transaction, &HashSet::new())
  1461. .unwrap();
  1462. let svm_instruction =
  1463. SVMInstruction::from(sanitized.message().instructions().first().unwrap());
  1464. invoke_context
  1465. .prepare_next_top_level_instruction(
  1466. &sanitized,
  1467. &svm_instruction,
  1468. 90,
  1469. svm_instruction.data,
  1470. )
  1471. .unwrap();
  1472. {
  1473. let instruction_context = invoke_context
  1474. .transaction_context
  1475. .get_next_instruction_context()
  1476. .unwrap();
  1477. for index_in_instruction in 2..account_metas.len() as IndexOfAccount {
  1478. let is_duplicate = instruction_context
  1479. .is_instruction_account_duplicate(index_in_instruction)
  1480. .unwrap();
  1481. if index_in_instruction % 2 == 0 {
  1482. assert!(is_duplicate.is_none());
  1483. } else {
  1484. assert_eq!(is_duplicate, Some(index_in_instruction.saturating_sub(1)));
  1485. }
  1486. }
  1487. }
  1488. invoke_context.transaction_context.push().unwrap();
  1489. let instruction = Instruction::new_with_bytes(
  1490. program_id,
  1491. &[20],
  1492. account_metas.iter().cloned().rev().collect(),
  1493. );
  1494. invoke_context
  1495. .prepare_next_instruction(instruction, &[fee_payer.pubkey()])
  1496. .unwrap();
  1497. let instruction_context = invoke_context
  1498. .transaction_context
  1499. .get_next_instruction_context()
  1500. .unwrap();
  1501. for index_in_instruction in 2..account_metas.len().saturating_sub(1) as u16 {
  1502. let is_duplicate = instruction_context
  1503. .is_instruction_account_duplicate(index_in_instruction)
  1504. .unwrap();
  1505. if index_in_instruction % 2 == 0 {
  1506. assert!(is_duplicate.is_none());
  1507. } else {
  1508. assert_eq!(is_duplicate, Some(index_in_instruction.saturating_sub(1)));
  1509. }
  1510. }
  1511. }
  1512. }