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