1#![deny(clippy::indexing_slicing)]
3#![cfg_attr(docsrs, feature(doc_auto_cfg))]
4
5#[cfg(not(target_os = "solana"))]
6use {solana_account::WritableAccount, solana_rent::Rent, std::mem::MaybeUninit};
7use {
8 solana_account::{AccountSharedData, ReadableAccount},
9 solana_instruction::error::InstructionError,
10 solana_instructions_sysvar as instructions,
11 solana_pubkey::Pubkey,
12 std::{
13 cell::{Ref, RefCell, RefMut},
14 collections::HashSet,
15 pin::Pin,
16 rc::Rc,
17 },
18};
19
20#[cfg(not(target_os = "solana"))]
22const MAX_PERMITTED_DATA_LENGTH: u64 = 10 * 1024 * 1024;
23#[cfg(test)]
24static_assertions::const_assert_eq!(
25 MAX_PERMITTED_DATA_LENGTH,
26 solana_system_interface::MAX_PERMITTED_DATA_LENGTH
27);
28
29#[cfg(not(target_os = "solana"))]
31const MAX_PERMITTED_ACCOUNTS_DATA_ALLOCATIONS_PER_TRANSACTION: i64 =
32 MAX_PERMITTED_DATA_LENGTH as i64 * 2;
33#[cfg(test)]
34static_assertions::const_assert_eq!(
35 MAX_PERMITTED_ACCOUNTS_DATA_ALLOCATIONS_PER_TRANSACTION,
36 solana_system_interface::MAX_PERMITTED_ACCOUNTS_DATA_ALLOCATIONS_PER_TRANSACTION
37);
38
39#[cfg(not(target_os = "solana"))]
41const MAX_PERMITTED_DATA_INCREASE: usize = 1_024 * 10;
42#[cfg(test)]
43static_assertions::const_assert_eq!(
44 MAX_PERMITTED_DATA_INCREASE,
45 solana_account_info::MAX_PERMITTED_DATA_INCREASE
46);
47
48pub type IndexOfAccount = u16;
50
51#[derive(Clone, Debug, Eq, PartialEq)]
55pub struct InstructionAccount {
56 pub index_in_transaction: IndexOfAccount,
58 pub index_in_caller: IndexOfAccount,
62 pub index_in_callee: IndexOfAccount,
66 pub is_signer: bool,
68 pub is_writable: bool,
70}
71
72pub type TransactionAccount = (Pubkey, AccountSharedData);
74
75#[derive(Clone, Debug, PartialEq)]
76pub struct TransactionAccounts {
77 accounts: Vec<RefCell<AccountSharedData>>,
78 touched_flags: RefCell<Box<[bool]>>,
79}
80
81impl TransactionAccounts {
82 #[cfg(not(target_os = "solana"))]
83 fn new(accounts: Vec<RefCell<AccountSharedData>>) -> TransactionAccounts {
84 TransactionAccounts {
85 touched_flags: RefCell::new(vec![false; accounts.len()].into_boxed_slice()),
86 accounts,
87 }
88 }
89
90 fn len(&self) -> usize {
91 self.accounts.len()
92 }
93
94 fn get(&self, index: IndexOfAccount) -> Option<&RefCell<AccountSharedData>> {
95 self.accounts.get(index as usize)
96 }
97
98 #[cfg(not(target_os = "solana"))]
99 pub fn touch(&self, index: IndexOfAccount) -> Result<(), InstructionError> {
100 *self
101 .touched_flags
102 .borrow_mut()
103 .get_mut(index as usize)
104 .ok_or(InstructionError::NotEnoughAccountKeys)? = true;
105 Ok(())
106 }
107
108 #[cfg(not(target_os = "solana"))]
109 pub fn touched_count(&self) -> usize {
110 self.touched_flags
111 .borrow()
112 .iter()
113 .fold(0usize, |accumulator, was_touched| {
114 accumulator.saturating_add(*was_touched as usize)
115 })
116 }
117
118 pub fn try_borrow(
119 &self,
120 index: IndexOfAccount,
121 ) -> Result<Ref<'_, AccountSharedData>, InstructionError> {
122 self.accounts
123 .get(index as usize)
124 .ok_or(InstructionError::MissingAccount)?
125 .try_borrow()
126 .map_err(|_| InstructionError::AccountBorrowFailed)
127 }
128
129 pub fn into_accounts(self) -> Vec<AccountSharedData> {
130 self.accounts
131 .into_iter()
132 .map(|account| account.into_inner())
133 .collect()
134 }
135}
136
137#[derive(Debug, Clone, PartialEq)]
141pub struct TransactionContext {
142 account_keys: Pin<Box<[Pubkey]>>,
143 accounts: Rc<TransactionAccounts>,
144 instruction_stack_capacity: usize,
145 instruction_trace_capacity: usize,
146 instruction_stack: Vec<usize>,
147 instruction_trace: Vec<InstructionContext>,
148 top_level_instruction_index: usize,
149 return_data: TransactionReturnData,
150 accounts_resize_delta: RefCell<i64>,
151 #[cfg(not(target_os = "solana"))]
152 remove_accounts_executable_flag_checks: bool,
153 #[cfg(not(target_os = "solana"))]
154 rent: Rent,
155}
156
157impl TransactionContext {
158 #[cfg(not(target_os = "solana"))]
160 pub fn new(
161 transaction_accounts: Vec<TransactionAccount>,
162 rent: Rent,
163 instruction_stack_capacity: usize,
164 instruction_trace_capacity: usize,
165 ) -> Self {
166 let (account_keys, accounts): (Vec<_>, Vec<_>) = transaction_accounts
167 .into_iter()
168 .map(|(key, account)| (key, RefCell::new(account)))
169 .unzip();
170 Self {
171 account_keys: Pin::new(account_keys.into_boxed_slice()),
172 accounts: Rc::new(TransactionAccounts::new(accounts)),
173 instruction_stack_capacity,
174 instruction_trace_capacity,
175 instruction_stack: Vec::with_capacity(instruction_stack_capacity),
176 instruction_trace: vec![InstructionContext::default()],
177 top_level_instruction_index: 0,
178 return_data: TransactionReturnData::default(),
179 accounts_resize_delta: RefCell::new(0),
180 remove_accounts_executable_flag_checks: true,
181 rent,
182 }
183 }
184
185 #[cfg(not(target_os = "solana"))]
186 pub fn set_remove_accounts_executable_flag_checks(&mut self, enabled: bool) {
187 self.remove_accounts_executable_flag_checks = enabled;
188 }
189
190 #[cfg(not(target_os = "solana"))]
192 pub fn deconstruct_without_keys(self) -> Result<Vec<AccountSharedData>, InstructionError> {
193 if !self.instruction_stack.is_empty() {
194 return Err(InstructionError::CallDepth);
195 }
196
197 Ok(Rc::try_unwrap(self.accounts)
198 .expect("transaction_context.accounts has unexpected outstanding refs")
199 .into_accounts())
200 }
201
202 #[cfg(not(target_os = "solana"))]
203 pub fn accounts(&self) -> &Rc<TransactionAccounts> {
204 &self.accounts
205 }
206
207 pub fn get_number_of_accounts(&self) -> IndexOfAccount {
209 self.accounts.len() as IndexOfAccount
210 }
211
212 pub fn get_key_of_account_at_index(
214 &self,
215 index_in_transaction: IndexOfAccount,
216 ) -> Result<&Pubkey, InstructionError> {
217 self.account_keys
218 .get(index_in_transaction as usize)
219 .ok_or(InstructionError::NotEnoughAccountKeys)
220 }
221
222 #[cfg(all(
224 not(target_os = "solana"),
225 any(test, feature = "dev-context-only-utils")
226 ))]
227 pub fn get_account_at_index(
228 &self,
229 index_in_transaction: IndexOfAccount,
230 ) -> Result<&RefCell<AccountSharedData>, InstructionError> {
231 self.accounts
232 .get(index_in_transaction)
233 .ok_or(InstructionError::NotEnoughAccountKeys)
234 }
235
236 pub fn find_index_of_account(&self, pubkey: &Pubkey) -> Option<IndexOfAccount> {
238 self.account_keys
239 .iter()
240 .position(|key| key == pubkey)
241 .map(|index| index as IndexOfAccount)
242 }
243
244 pub fn find_index_of_program_account(&self, pubkey: &Pubkey) -> Option<IndexOfAccount> {
246 self.account_keys
247 .iter()
248 .rposition(|key| key == pubkey)
249 .map(|index| index as IndexOfAccount)
250 }
251
252 pub fn get_instruction_trace_capacity(&self) -> usize {
254 self.instruction_trace_capacity
255 }
256
257 pub fn get_instruction_trace_length(&self) -> usize {
262 self.instruction_trace.len().saturating_sub(1)
263 }
264
265 pub fn get_instruction_context_at_index_in_trace(
267 &self,
268 index_in_trace: usize,
269 ) -> Result<&InstructionContext, InstructionError> {
270 self.instruction_trace
271 .get(index_in_trace)
272 .ok_or(InstructionError::CallDepth)
273 }
274
275 pub fn get_instruction_context_at_nesting_level(
277 &self,
278 nesting_level: usize,
279 ) -> Result<&InstructionContext, InstructionError> {
280 let index_in_trace = *self
281 .instruction_stack
282 .get(nesting_level)
283 .ok_or(InstructionError::CallDepth)?;
284 let instruction_context = self.get_instruction_context_at_index_in_trace(index_in_trace)?;
285 debug_assert_eq!(instruction_context.nesting_level, nesting_level);
286 Ok(instruction_context)
287 }
288
289 pub fn get_instruction_stack_capacity(&self) -> usize {
291 self.instruction_stack_capacity
292 }
293
294 pub fn get_instruction_context_stack_height(&self) -> usize {
297 self.instruction_stack.len()
298 }
299
300 pub fn get_current_instruction_context(&self) -> Result<&InstructionContext, InstructionError> {
302 let level = self
303 .get_instruction_context_stack_height()
304 .checked_sub(1)
305 .ok_or(InstructionError::CallDepth)?;
306 self.get_instruction_context_at_nesting_level(level)
307 }
308
309 pub fn get_next_instruction_context(
313 &mut self,
314 ) -> Result<&mut InstructionContext, InstructionError> {
315 self.instruction_trace
316 .last_mut()
317 .ok_or(InstructionError::CallDepth)
318 }
319
320 #[cfg(not(target_os = "solana"))]
322 pub fn push(&mut self) -> Result<(), InstructionError> {
323 let nesting_level = self.get_instruction_context_stack_height();
324 let caller_instruction_context = self
325 .instruction_trace
326 .last()
327 .ok_or(InstructionError::CallDepth)?;
328 let callee_instruction_accounts_lamport_sum =
329 self.instruction_accounts_lamport_sum(caller_instruction_context)?;
330 if !self.instruction_stack.is_empty() {
331 let caller_instruction_context = self.get_current_instruction_context()?;
332 let original_caller_instruction_accounts_lamport_sum =
333 caller_instruction_context.instruction_accounts_lamport_sum;
334 let current_caller_instruction_accounts_lamport_sum =
335 self.instruction_accounts_lamport_sum(caller_instruction_context)?;
336 if original_caller_instruction_accounts_lamport_sum
337 != current_caller_instruction_accounts_lamport_sum
338 {
339 return Err(InstructionError::UnbalancedInstruction);
340 }
341 }
342 {
343 let instruction_context = self.get_next_instruction_context()?;
344 instruction_context.nesting_level = nesting_level;
345 instruction_context.instruction_accounts_lamport_sum =
346 callee_instruction_accounts_lamport_sum;
347 }
348 let index_in_trace = self.get_instruction_trace_length();
349 if index_in_trace >= self.instruction_trace_capacity {
350 return Err(InstructionError::MaxInstructionTraceLengthExceeded);
351 }
352 self.instruction_trace.push(InstructionContext::default());
353 if nesting_level >= self.instruction_stack_capacity {
354 return Err(InstructionError::CallDepth);
355 }
356 self.instruction_stack.push(index_in_trace);
357 if let Some(index_in_transaction) = self.find_index_of_account(&instructions::id()) {
358 let mut mut_account_ref = self
359 .accounts
360 .get(index_in_transaction)
361 .ok_or(InstructionError::NotEnoughAccountKeys)?
362 .try_borrow_mut()
363 .map_err(|_| InstructionError::AccountBorrowFailed)?;
364 instructions::store_current_index(
365 mut_account_ref.data_as_mut_slice(),
366 self.top_level_instruction_index as u16,
367 );
368 }
369 Ok(())
370 }
371
372 #[cfg(not(target_os = "solana"))]
374 pub fn pop(&mut self) -> Result<(), InstructionError> {
375 if self.instruction_stack.is_empty() {
376 return Err(InstructionError::CallDepth);
377 }
378 let detected_an_unbalanced_instruction =
380 self.get_current_instruction_context()
381 .and_then(|instruction_context| {
382 for index_in_transaction in instruction_context.program_accounts.iter() {
384 self.accounts
385 .get(*index_in_transaction)
386 .ok_or(InstructionError::NotEnoughAccountKeys)?
387 .try_borrow_mut()
388 .map_err(|_| InstructionError::AccountBorrowOutstanding)?;
389 }
390 self.instruction_accounts_lamport_sum(instruction_context)
391 .map(|instruction_accounts_lamport_sum| {
392 instruction_context.instruction_accounts_lamport_sum
393 != instruction_accounts_lamport_sum
394 })
395 });
396 self.instruction_stack.pop();
398 if self.instruction_stack.is_empty() {
399 self.top_level_instruction_index = self.top_level_instruction_index.saturating_add(1);
400 }
401 if detected_an_unbalanced_instruction? {
402 Err(InstructionError::UnbalancedInstruction)
403 } else {
404 Ok(())
405 }
406 }
407
408 pub fn get_return_data(&self) -> (&Pubkey, &[u8]) {
410 (&self.return_data.program_id, &self.return_data.data)
411 }
412
413 pub fn set_return_data(
415 &mut self,
416 program_id: Pubkey,
417 data: Vec<u8>,
418 ) -> Result<(), InstructionError> {
419 self.return_data = TransactionReturnData { program_id, data };
420 Ok(())
421 }
422
423 #[cfg(not(target_os = "solana"))]
425 fn instruction_accounts_lamport_sum(
426 &self,
427 instruction_context: &InstructionContext,
428 ) -> Result<u128, InstructionError> {
429 let mut instruction_accounts_lamport_sum: u128 = 0;
430 for instruction_account_index in 0..instruction_context.get_number_of_instruction_accounts()
431 {
432 if instruction_context
433 .is_instruction_account_duplicate(instruction_account_index)?
434 .is_some()
435 {
436 continue; }
438 let index_in_transaction = instruction_context
439 .get_index_of_instruction_account_in_transaction(instruction_account_index)?;
440 instruction_accounts_lamport_sum = (self
441 .accounts
442 .get(index_in_transaction)
443 .ok_or(InstructionError::NotEnoughAccountKeys)?
444 .try_borrow()
445 .map_err(|_| InstructionError::AccountBorrowOutstanding)?
446 .lamports() as u128)
447 .checked_add(instruction_accounts_lamport_sum)
448 .ok_or(InstructionError::ArithmeticOverflow)?;
449 }
450 Ok(instruction_accounts_lamport_sum)
451 }
452
453 pub fn accounts_resize_delta(&self) -> Result<i64, InstructionError> {
455 self.accounts_resize_delta
456 .try_borrow()
457 .map_err(|_| InstructionError::GenericError)
458 .map(|value_ref| *value_ref)
459 }
460
461 pub fn account_data_write_access_handler(&self) -> Box<dyn Fn(u64) -> Result<u64, ()>> {
463 let accounts = Rc::clone(&self.accounts);
464 Box::new(move |index_in_transaction| {
465 let mut account = accounts
469 .accounts
470 .get(index_in_transaction as usize)
471 .ok_or(())?
472 .try_borrow_mut()
473 .map_err(|_| ())?;
474 accounts
475 .touch(index_in_transaction as IndexOfAccount)
476 .map_err(|_| ())?;
477
478 if account.is_shared() {
479 account.reserve(MAX_PERMITTED_DATA_INCREASE);
482 }
483 Ok(account.data_as_mut_slice().as_mut_ptr() as u64)
484 })
485 }
486}
487
488#[cfg_attr(
490 feature = "serde",
491 derive(serde_derive::Deserialize, serde_derive::Serialize)
492)]
493#[derive(Clone, Debug, Default, PartialEq, Eq)]
494pub struct TransactionReturnData {
495 pub program_id: Pubkey,
496 pub data: Vec<u8>,
497}
498
499#[derive(Debug, Clone, Default, Eq, PartialEq)]
503pub struct InstructionContext {
504 nesting_level: usize,
505 instruction_accounts_lamport_sum: u128,
506 program_accounts: Vec<IndexOfAccount>,
507 instruction_accounts: Vec<InstructionAccount>,
508 instruction_data: Vec<u8>,
509}
510
511impl InstructionContext {
512 #[cfg(not(target_os = "solana"))]
514 pub fn configure(
515 &mut self,
516 program_accounts: &[IndexOfAccount],
517 instruction_accounts: &[InstructionAccount],
518 instruction_data: &[u8],
519 ) {
520 self.program_accounts = program_accounts.to_vec();
521 self.instruction_accounts = instruction_accounts.to_vec();
522 self.instruction_data = instruction_data.to_vec();
523 }
524
525 pub fn get_stack_height(&self) -> usize {
529 self.nesting_level.saturating_add(1)
530 }
531
532 pub fn get_number_of_program_accounts(&self) -> IndexOfAccount {
534 self.program_accounts.len() as IndexOfAccount
535 }
536
537 pub fn get_number_of_instruction_accounts(&self) -> IndexOfAccount {
539 self.instruction_accounts.len() as IndexOfAccount
540 }
541
542 pub fn check_number_of_instruction_accounts(
544 &self,
545 expected_at_least: IndexOfAccount,
546 ) -> Result<(), InstructionError> {
547 if self.get_number_of_instruction_accounts() < expected_at_least {
548 Err(InstructionError::NotEnoughAccountKeys)
549 } else {
550 Ok(())
551 }
552 }
553
554 pub fn get_instruction_data(&self) -> &[u8] {
556 &self.instruction_data
557 }
558
559 pub fn find_index_of_program_account(
561 &self,
562 transaction_context: &TransactionContext,
563 pubkey: &Pubkey,
564 ) -> Option<IndexOfAccount> {
565 self.program_accounts
566 .iter()
567 .position(|index_in_transaction| {
568 transaction_context
569 .account_keys
570 .get(*index_in_transaction as usize)
571 == Some(pubkey)
572 })
573 .map(|index| index as IndexOfAccount)
574 }
575
576 pub fn find_index_of_instruction_account(
578 &self,
579 transaction_context: &TransactionContext,
580 pubkey: &Pubkey,
581 ) -> Option<IndexOfAccount> {
582 self.instruction_accounts
583 .iter()
584 .position(|instruction_account| {
585 transaction_context
586 .account_keys
587 .get(instruction_account.index_in_transaction as usize)
588 == Some(pubkey)
589 })
590 .map(|index| index as IndexOfAccount)
591 }
592
593 pub fn get_index_of_program_account_in_transaction(
595 &self,
596 program_account_index: IndexOfAccount,
597 ) -> Result<IndexOfAccount, InstructionError> {
598 Ok(*self
599 .program_accounts
600 .get(program_account_index as usize)
601 .ok_or(InstructionError::NotEnoughAccountKeys)?)
602 }
603
604 pub fn get_index_of_instruction_account_in_transaction(
606 &self,
607 instruction_account_index: IndexOfAccount,
608 ) -> Result<IndexOfAccount, InstructionError> {
609 Ok(self
610 .instruction_accounts
611 .get(instruction_account_index as usize)
612 .ok_or(InstructionError::NotEnoughAccountKeys)?
613 .index_in_transaction as IndexOfAccount)
614 }
615
616 pub fn is_instruction_account_duplicate(
619 &self,
620 instruction_account_index: IndexOfAccount,
621 ) -> Result<Option<IndexOfAccount>, InstructionError> {
622 let index_in_callee = self
623 .instruction_accounts
624 .get(instruction_account_index as usize)
625 .ok_or(InstructionError::NotEnoughAccountKeys)?
626 .index_in_callee;
627 Ok(if index_in_callee == instruction_account_index {
628 None
629 } else {
630 Some(index_in_callee)
631 })
632 }
633
634 pub fn get_last_program_key<'a, 'b: 'a>(
636 &'a self,
637 transaction_context: &'b TransactionContext,
638 ) -> Result<&'b Pubkey, InstructionError> {
639 self.get_index_of_program_account_in_transaction(
640 self.get_number_of_program_accounts().saturating_sub(1),
641 )
642 .and_then(|index_in_transaction| {
643 transaction_context.get_key_of_account_at_index(index_in_transaction)
644 })
645 }
646
647 fn try_borrow_account<'a, 'b: 'a>(
648 &'a self,
649 transaction_context: &'b TransactionContext,
650 index_in_transaction: IndexOfAccount,
651 index_in_instruction: IndexOfAccount,
652 ) -> Result<BorrowedAccount<'a>, InstructionError> {
653 let account = transaction_context
654 .accounts
655 .get(index_in_transaction)
656 .ok_or(InstructionError::MissingAccount)?
657 .try_borrow_mut()
658 .map_err(|_| InstructionError::AccountBorrowFailed)?;
659 Ok(BorrowedAccount {
660 transaction_context,
661 instruction_context: self,
662 index_in_transaction,
663 index_in_instruction,
664 account,
665 })
666 }
667
668 pub fn try_borrow_last_program_account<'a, 'b: 'a>(
670 &'a self,
671 transaction_context: &'b TransactionContext,
672 ) -> Result<BorrowedAccount<'a>, InstructionError> {
673 let result = self.try_borrow_program_account(
674 transaction_context,
675 self.get_number_of_program_accounts().saturating_sub(1),
676 );
677 debug_assert!(result.is_ok());
678 result
679 }
680
681 pub fn try_borrow_program_account<'a, 'b: 'a>(
683 &'a self,
684 transaction_context: &'b TransactionContext,
685 program_account_index: IndexOfAccount,
686 ) -> Result<BorrowedAccount<'a>, InstructionError> {
687 let index_in_transaction =
688 self.get_index_of_program_account_in_transaction(program_account_index)?;
689 self.try_borrow_account(
690 transaction_context,
691 index_in_transaction,
692 program_account_index,
693 )
694 }
695
696 pub fn try_borrow_instruction_account<'a, 'b: 'a>(
698 &'a self,
699 transaction_context: &'b TransactionContext,
700 instruction_account_index: IndexOfAccount,
701 ) -> Result<BorrowedAccount<'a>, InstructionError> {
702 let index_in_transaction =
703 self.get_index_of_instruction_account_in_transaction(instruction_account_index)?;
704 self.try_borrow_account(
705 transaction_context,
706 index_in_transaction,
707 self.get_number_of_program_accounts()
708 .saturating_add(instruction_account_index),
709 )
710 }
711
712 pub fn is_instruction_account_signer(
714 &self,
715 instruction_account_index: IndexOfAccount,
716 ) -> Result<bool, InstructionError> {
717 Ok(self
718 .instruction_accounts
719 .get(instruction_account_index as usize)
720 .ok_or(InstructionError::MissingAccount)?
721 .is_signer)
722 }
723
724 pub fn is_instruction_account_writable(
726 &self,
727 instruction_account_index: IndexOfAccount,
728 ) -> Result<bool, InstructionError> {
729 Ok(self
730 .instruction_accounts
731 .get(instruction_account_index as usize)
732 .ok_or(InstructionError::MissingAccount)?
733 .is_writable)
734 }
735
736 pub fn get_signers(
738 &self,
739 transaction_context: &TransactionContext,
740 ) -> Result<HashSet<Pubkey>, InstructionError> {
741 let mut result = HashSet::new();
742 for instruction_account in self.instruction_accounts.iter() {
743 if instruction_account.is_signer {
744 result.insert(
745 *transaction_context
746 .get_key_of_account_at_index(instruction_account.index_in_transaction)?,
747 );
748 }
749 }
750 Ok(result)
751 }
752}
753
754#[derive(Debug)]
756pub struct BorrowedAccount<'a> {
757 transaction_context: &'a TransactionContext,
758 instruction_context: &'a InstructionContext,
759 index_in_transaction: IndexOfAccount,
760 index_in_instruction: IndexOfAccount,
761 account: RefMut<'a, AccountSharedData>,
762}
763
764impl BorrowedAccount<'_> {
765 pub fn transaction_context(&self) -> &TransactionContext {
767 self.transaction_context
768 }
769
770 #[inline]
772 pub fn get_index_in_transaction(&self) -> IndexOfAccount {
773 self.index_in_transaction
774 }
775
776 #[inline]
778 pub fn get_key(&self) -> &Pubkey {
779 self.transaction_context
780 .get_key_of_account_at_index(self.index_in_transaction)
781 .unwrap()
782 }
783
784 #[inline]
786 pub fn get_owner(&self) -> &Pubkey {
787 self.account.owner()
788 }
789
790 #[cfg(not(target_os = "solana"))]
792 pub fn set_owner(&mut self, pubkey: &[u8]) -> Result<(), InstructionError> {
793 if !self.is_owned_by_current_program() {
795 return Err(InstructionError::ModifiedProgramId);
796 }
797 if !self.is_writable() {
799 return Err(InstructionError::ModifiedProgramId);
800 }
801 if self.is_executable_internal() {
803 return Err(InstructionError::ModifiedProgramId);
804 }
805 if !is_zeroed(self.get_data()) {
807 return Err(InstructionError::ModifiedProgramId);
808 }
809 if self.get_owner().to_bytes() == pubkey {
811 return Ok(());
812 }
813 self.touch()?;
814 self.account.copy_into_owner_from_slice(pubkey);
815 Ok(())
816 }
817
818 #[inline]
820 pub fn get_lamports(&self) -> u64 {
821 self.account.lamports()
822 }
823
824 #[cfg(not(target_os = "solana"))]
826 pub fn set_lamports(&mut self, lamports: u64) -> Result<(), InstructionError> {
827 if !self.is_owned_by_current_program() && lamports < self.get_lamports() {
829 return Err(InstructionError::ExternalAccountLamportSpend);
830 }
831 if !self.is_writable() {
833 return Err(InstructionError::ReadonlyLamportChange);
834 }
835 if self.is_executable_internal() {
837 return Err(InstructionError::ExecutableLamportChange);
838 }
839 if self.get_lamports() == lamports {
841 return Ok(());
842 }
843 self.touch()?;
844 self.account.set_lamports(lamports);
845 Ok(())
846 }
847
848 #[cfg(not(target_os = "solana"))]
850 pub fn checked_add_lamports(&mut self, lamports: u64) -> Result<(), InstructionError> {
851 self.set_lamports(
852 self.get_lamports()
853 .checked_add(lamports)
854 .ok_or(InstructionError::ArithmeticOverflow)?,
855 )
856 }
857
858 #[cfg(not(target_os = "solana"))]
860 pub fn checked_sub_lamports(&mut self, lamports: u64) -> Result<(), InstructionError> {
861 self.set_lamports(
862 self.get_lamports()
863 .checked_sub(lamports)
864 .ok_or(InstructionError::ArithmeticOverflow)?,
865 )
866 }
867
868 #[inline]
870 pub fn get_data(&self) -> &[u8] {
871 self.account.data()
872 }
873
874 #[cfg(not(target_os = "solana"))]
876 pub fn get_data_mut(&mut self) -> Result<&mut [u8], InstructionError> {
877 self.can_data_be_changed()?;
878 self.touch()?;
879 self.make_data_mut();
880 Ok(self.account.data_as_mut_slice())
881 }
882
883 #[cfg(not(target_os = "solana"))]
888 pub fn spare_data_capacity_mut(&mut self) -> Result<&mut [MaybeUninit<u8>], InstructionError> {
889 debug_assert!(!self.account.is_shared());
890 Ok(self.account.spare_data_capacity_mut())
891 }
892
893 #[cfg(all(
899 not(target_os = "solana"),
900 any(test, feature = "dev-context-only-utils")
901 ))]
902 pub fn set_data(&mut self, data: Vec<u8>) -> Result<(), InstructionError> {
903 self.can_data_be_resized(data.len())?;
904 self.can_data_be_changed()?;
905 self.touch()?;
906
907 self.update_accounts_resize_delta(data.len())?;
908 self.account.set_data(data);
909 Ok(())
910 }
911
912 #[cfg(not(target_os = "solana"))]
917 pub fn set_data_from_slice(&mut self, data: &[u8]) -> Result<(), InstructionError> {
918 self.can_data_be_resized(data.len())?;
919 self.can_data_be_changed()?;
920 self.touch()?;
921 self.update_accounts_resize_delta(data.len())?;
922 self.account.set_data_from_slice(data);
927
928 Ok(())
929 }
930
931 #[cfg(not(target_os = "solana"))]
935 pub fn set_data_length(&mut self, new_length: usize) -> Result<(), InstructionError> {
936 self.can_data_be_resized(new_length)?;
937 self.can_data_be_changed()?;
938 if self.get_data().len() == new_length {
940 return Ok(());
941 }
942 self.touch()?;
943 self.update_accounts_resize_delta(new_length)?;
944 self.account.resize(new_length, 0);
945 Ok(())
946 }
947
948 #[cfg(not(target_os = "solana"))]
950 pub fn extend_from_slice(&mut self, data: &[u8]) -> Result<(), InstructionError> {
951 let new_len = self.get_data().len().saturating_add(data.len());
952 self.can_data_be_resized(new_len)?;
953 self.can_data_be_changed()?;
954
955 if data.is_empty() {
956 return Ok(());
957 }
958
959 self.touch()?;
960 self.update_accounts_resize_delta(new_len)?;
961 self.make_data_mut();
965 self.account.extend_from_slice(data);
966 Ok(())
967 }
968
969 #[cfg(not(target_os = "solana"))]
972 pub fn reserve(&mut self, additional: usize) -> Result<(), InstructionError> {
973 self.make_data_mut();
978 self.account.reserve(additional);
979
980 Ok(())
981 }
982
983 #[cfg(not(target_os = "solana"))]
985 pub fn capacity(&self) -> usize {
986 self.account.capacity()
987 }
988
989 #[cfg(not(target_os = "solana"))]
997 pub fn is_shared(&self) -> bool {
998 self.account.is_shared()
999 }
1000
1001 #[cfg(not(target_os = "solana"))]
1002 fn make_data_mut(&mut self) {
1003 if self.account.is_shared() {
1012 self.account.reserve(MAX_PERMITTED_DATA_INCREASE);
1013 }
1014 }
1015
1016 #[cfg(all(not(target_os = "solana"), feature = "bincode"))]
1018 pub fn get_state<T: serde::de::DeserializeOwned>(&self) -> Result<T, InstructionError> {
1019 self.account
1020 .deserialize_data()
1021 .map_err(|_| InstructionError::InvalidAccountData)
1022 }
1023
1024 #[cfg(all(not(target_os = "solana"), feature = "bincode"))]
1026 pub fn set_state<T: serde::Serialize>(&mut self, state: &T) -> Result<(), InstructionError> {
1027 let data = self.get_data_mut()?;
1028 let serialized_size =
1029 bincode::serialized_size(state).map_err(|_| InstructionError::GenericError)?;
1030 if serialized_size > data.len() as u64 {
1031 return Err(InstructionError::AccountDataTooSmall);
1032 }
1033 bincode::serialize_into(&mut *data, state).map_err(|_| InstructionError::GenericError)?;
1034 Ok(())
1035 }
1036
1037 #[cfg(not(target_os = "solana"))]
1040 pub fn is_rent_exempt_at_data_length(&self, data_length: usize) -> bool {
1041 self.transaction_context
1042 .rent
1043 .is_exempt(self.get_lamports(), data_length)
1044 }
1045
1046 #[inline]
1048 #[deprecated(since = "2.1.0", note = "Use `get_owner` instead")]
1049 pub fn is_executable(&self) -> bool {
1050 self.account.executable()
1051 }
1052
1053 #[cfg(not(target_os = "solana"))]
1055 #[inline]
1056 fn is_executable_internal(&self) -> bool {
1057 !self
1058 .transaction_context
1059 .remove_accounts_executable_flag_checks
1060 && self.account.executable()
1061 }
1062
1063 #[cfg(not(target_os = "solana"))]
1065 pub fn set_executable(&mut self, is_executable: bool) -> Result<(), InstructionError> {
1066 if !self
1068 .transaction_context
1069 .rent
1070 .is_exempt(self.get_lamports(), self.get_data().len())
1071 {
1072 return Err(InstructionError::ExecutableAccountNotRentExempt);
1073 }
1074 if !self.is_owned_by_current_program() {
1076 return Err(InstructionError::ExecutableModified);
1077 }
1078 if !self.is_writable() {
1080 return Err(InstructionError::ExecutableModified);
1081 }
1082 if self.is_executable_internal() && !is_executable {
1084 return Err(InstructionError::ExecutableModified);
1085 }
1086 #[allow(deprecated)]
1088 if self.is_executable() == is_executable {
1089 return Ok(());
1090 }
1091 self.touch()?;
1092 self.account.set_executable(is_executable);
1093 Ok(())
1094 }
1095
1096 #[cfg(not(target_os = "solana"))]
1098 #[inline]
1099 pub fn get_rent_epoch(&self) -> u64 {
1100 self.account.rent_epoch()
1101 }
1102
1103 pub fn is_signer(&self) -> bool {
1105 if self.index_in_instruction < self.instruction_context.get_number_of_program_accounts() {
1106 return false;
1107 }
1108 self.instruction_context
1109 .is_instruction_account_signer(
1110 self.index_in_instruction
1111 .saturating_sub(self.instruction_context.get_number_of_program_accounts()),
1112 )
1113 .unwrap_or_default()
1114 }
1115
1116 pub fn is_writable(&self) -> bool {
1118 if self.index_in_instruction < self.instruction_context.get_number_of_program_accounts() {
1119 return false;
1120 }
1121 self.instruction_context
1122 .is_instruction_account_writable(
1123 self.index_in_instruction
1124 .saturating_sub(self.instruction_context.get_number_of_program_accounts()),
1125 )
1126 .unwrap_or_default()
1127 }
1128
1129 pub fn is_owned_by_current_program(&self) -> bool {
1131 self.instruction_context
1132 .get_last_program_key(self.transaction_context)
1133 .map(|key| key == self.get_owner())
1134 .unwrap_or_default()
1135 }
1136
1137 #[cfg(not(target_os = "solana"))]
1139 pub fn can_data_be_changed(&self) -> Result<(), InstructionError> {
1140 if self.is_executable_internal() {
1142 return Err(InstructionError::ExecutableDataModified);
1143 }
1144 if !self.is_writable() {
1146 return Err(InstructionError::ReadonlyDataModified);
1147 }
1148 if !self.is_owned_by_current_program() {
1150 return Err(InstructionError::ExternalAccountDataModified);
1151 }
1152 Ok(())
1153 }
1154
1155 #[cfg(not(target_os = "solana"))]
1157 pub fn can_data_be_resized(&self, new_length: usize) -> Result<(), InstructionError> {
1158 let old_length = self.get_data().len();
1159 if new_length != old_length && !self.is_owned_by_current_program() {
1161 return Err(InstructionError::AccountDataSizeChanged);
1162 }
1163 if new_length > MAX_PERMITTED_DATA_LENGTH as usize {
1165 return Err(InstructionError::InvalidRealloc);
1166 }
1167 let length_delta = (new_length as i64).saturating_sub(old_length as i64);
1169 if self
1170 .transaction_context
1171 .accounts_resize_delta()?
1172 .saturating_add(length_delta)
1173 > MAX_PERMITTED_ACCOUNTS_DATA_ALLOCATIONS_PER_TRANSACTION
1174 {
1175 return Err(InstructionError::MaxAccountsDataAllocationsExceeded);
1176 }
1177 Ok(())
1178 }
1179
1180 #[cfg(not(target_os = "solana"))]
1181 fn touch(&self) -> Result<(), InstructionError> {
1182 self.transaction_context
1183 .accounts()
1184 .touch(self.index_in_transaction)
1185 }
1186
1187 #[cfg(not(target_os = "solana"))]
1188 fn update_accounts_resize_delta(&mut self, new_len: usize) -> Result<(), InstructionError> {
1189 let mut accounts_resize_delta = self
1190 .transaction_context
1191 .accounts_resize_delta
1192 .try_borrow_mut()
1193 .map_err(|_| InstructionError::GenericError)?;
1194 *accounts_resize_delta = accounts_resize_delta
1195 .saturating_add((new_len as i64).saturating_sub(self.get_data().len() as i64));
1196 Ok(())
1197 }
1198}
1199
1200#[cfg(not(target_os = "solana"))]
1202pub struct ExecutionRecord {
1203 pub accounts: Vec<TransactionAccount>,
1204 pub return_data: TransactionReturnData,
1205 pub touched_account_count: u64,
1206 pub accounts_resize_delta: i64,
1207}
1208
1209#[cfg(not(target_os = "solana"))]
1211impl From<TransactionContext> for ExecutionRecord {
1212 fn from(context: TransactionContext) -> Self {
1213 let accounts = Rc::try_unwrap(context.accounts)
1214 .expect("transaction_context.accounts has unexpected outstanding refs");
1215 let touched_account_count = accounts.touched_count() as u64;
1216 let accounts = accounts.into_accounts();
1217 Self {
1218 accounts: Vec::from(Pin::into_inner(context.account_keys))
1219 .into_iter()
1220 .zip(accounts)
1221 .collect(),
1222 return_data: context.return_data,
1223 touched_account_count,
1224 accounts_resize_delta: RefCell::into_inner(context.accounts_resize_delta),
1225 }
1226 }
1227}
1228
1229#[cfg(not(target_os = "solana"))]
1230fn is_zeroed(buf: &[u8]) -> bool {
1231 const ZEROS_LEN: usize = 1024;
1232 const ZEROS: [u8; ZEROS_LEN] = [0; ZEROS_LEN];
1233 let mut chunks = buf.chunks_exact(ZEROS_LEN);
1234
1235 #[allow(clippy::indexing_slicing)]
1236 {
1237 chunks.all(|chunk| chunk == &ZEROS[..])
1238 && chunks.remainder() == &ZEROS[..chunks.remainder().len()]
1239 }
1240}