1#![deny(clippy::indexing_slicing)]
3
4#[cfg(all(not(target_os = "solana"), feature = "full", debug_assertions))]
5use crate::signature::Signature;
6use {
7 crate::{instruction::InstructionError, pubkey::Pubkey},
8 solana_account::{AccountSharedData, ReadableAccount},
9 std::{
10 cell::{Ref, RefCell, RefMut},
11 collections::HashSet,
12 pin::Pin,
13 rc::Rc,
14 },
15};
16#[cfg(not(target_os = "solana"))]
17use {
18 crate::{
19 rent::Rent,
20 system_instruction::{
21 MAX_PERMITTED_ACCOUNTS_DATA_ALLOCATIONS_PER_TRANSACTION, MAX_PERMITTED_DATA_LENGTH,
22 },
23 },
24 solana_account::WritableAccount,
25 solana_program::entrypoint::MAX_PERMITTED_DATA_INCREASE,
26 std::mem::MaybeUninit,
27};
28
29pub type IndexOfAccount = u16;
31
32#[derive(Clone, Debug, Eq, PartialEq)]
36pub struct InstructionAccount {
37 pub index_in_transaction: IndexOfAccount,
39 pub index_in_caller: IndexOfAccount,
43 pub index_in_callee: IndexOfAccount,
47 pub is_signer: bool,
49 pub is_writable: bool,
51}
52
53pub type TransactionAccount = (Pubkey, AccountSharedData);
55
56#[derive(Clone, Debug, PartialEq)]
57pub struct TransactionAccounts {
58 accounts: Vec<RefCell<AccountSharedData>>,
59 touched_flags: RefCell<Box<[bool]>>,
60}
61
62impl TransactionAccounts {
63 #[cfg(not(target_os = "solana"))]
64 fn new(accounts: Vec<RefCell<AccountSharedData>>) -> TransactionAccounts {
65 TransactionAccounts {
66 touched_flags: RefCell::new(vec![false; accounts.len()].into_boxed_slice()),
67 accounts,
68 }
69 }
70
71 fn len(&self) -> usize {
72 self.accounts.len()
73 }
74
75 pub fn get(&self, index: IndexOfAccount) -> Option<&RefCell<AccountSharedData>> {
76 self.accounts.get(index as usize)
77 }
78
79 #[cfg(not(target_os = "solana"))]
80 pub fn touch(&self, index: IndexOfAccount) -> Result<(), InstructionError> {
81 *self
82 .touched_flags
83 .borrow_mut()
84 .get_mut(index as usize)
85 .ok_or(InstructionError::NotEnoughAccountKeys)? = true;
86 Ok(())
87 }
88
89 #[cfg(not(target_os = "solana"))]
90 pub fn touched_count(&self) -> usize {
91 self.touched_flags
92 .borrow()
93 .iter()
94 .fold(0usize, |accumulator, was_touched| {
95 accumulator.saturating_add(*was_touched as usize)
96 })
97 }
98
99 pub fn try_borrow(
100 &self,
101 index: IndexOfAccount,
102 ) -> Result<Ref<'_, AccountSharedData>, InstructionError> {
103 self.accounts
104 .get(index as usize)
105 .ok_or(InstructionError::MissingAccount)?
106 .try_borrow()
107 .map_err(|_| InstructionError::AccountBorrowFailed)
108 }
109
110 pub fn try_borrow_mut(
111 &self,
112 index: IndexOfAccount,
113 ) -> Result<RefMut<'_, AccountSharedData>, InstructionError> {
114 self.accounts
115 .get(index as usize)
116 .ok_or(InstructionError::MissingAccount)?
117 .try_borrow_mut()
118 .map_err(|_| InstructionError::AccountBorrowFailed)
119 }
120
121 pub fn into_accounts(self) -> Vec<AccountSharedData> {
122 self.accounts
123 .into_iter()
124 .map(|account| account.into_inner())
125 .collect()
126 }
127}
128
129#[derive(Debug, Clone, PartialEq)]
133pub struct TransactionContext {
134 account_keys: Pin<Box<[Pubkey]>>,
135 accounts: Rc<TransactionAccounts>,
136 instruction_stack_capacity: usize,
137 instruction_trace_capacity: usize,
138 instruction_stack: Vec<usize>,
139 instruction_trace: Vec<InstructionContext>,
140 return_data: TransactionReturnData,
141 accounts_resize_delta: RefCell<i64>,
142 #[cfg(not(target_os = "solana"))]
143 rent: Rent,
144 #[cfg(all(not(target_os = "solana"), feature = "full", debug_assertions))]
146 signature: Signature,
147}
148
149impl TransactionContext {
150 #[cfg(not(target_os = "solana"))]
152 pub fn new(
153 transaction_accounts: Vec<TransactionAccount>,
154 rent: Rent,
155 instruction_stack_capacity: usize,
156 instruction_trace_capacity: usize,
157 ) -> Self {
158 let (account_keys, accounts): (Vec<_>, Vec<_>) = transaction_accounts
159 .into_iter()
160 .map(|(key, account)| (key, RefCell::new(account)))
161 .unzip();
162 Self {
163 account_keys: Pin::new(account_keys.into_boxed_slice()),
164 accounts: Rc::new(TransactionAccounts::new(accounts)),
165 instruction_stack_capacity,
166 instruction_trace_capacity,
167 instruction_stack: Vec::with_capacity(instruction_stack_capacity),
168 instruction_trace: vec![InstructionContext::default()],
169 return_data: TransactionReturnData::default(),
170 accounts_resize_delta: RefCell::new(0),
171 rent,
172 #[cfg(all(not(target_os = "solana"), feature = "full", debug_assertions))]
173 signature: Signature::default(),
174 }
175 }
176
177 #[cfg(not(target_os = "solana"))]
179 pub fn deconstruct_without_keys(self) -> Result<Vec<AccountSharedData>, InstructionError> {
180 if !self.instruction_stack.is_empty() {
181 return Err(InstructionError::CallDepth);
182 }
183
184 Ok(Rc::try_unwrap(self.accounts)
185 .expect("transaction_context.accounts has unexpected outstanding refs")
186 .into_accounts())
187 }
188
189 #[cfg(not(target_os = "solana"))]
190 pub fn accounts(&self) -> &Rc<TransactionAccounts> {
191 &self.accounts
192 }
193
194 #[cfg(all(not(target_os = "solana"), feature = "full", debug_assertions))]
196 pub fn set_signature(&mut self, signature: &Signature) {
197 self.signature = *signature;
198 }
199
200 #[cfg(all(not(target_os = "solana"), feature = "full", debug_assertions))]
202 pub fn get_signature(&self) -> &Signature {
203 &self.signature
204 }
205
206 pub fn get_number_of_accounts(&self) -> IndexOfAccount {
208 self.accounts.len() as IndexOfAccount
209 }
210
211 pub fn get_key_of_account_at_index(
213 &self,
214 index_in_transaction: IndexOfAccount,
215 ) -> Result<&Pubkey, InstructionError> {
216 self.account_keys
217 .get(index_in_transaction as usize)
218 .ok_or(InstructionError::NotEnoughAccountKeys)
219 }
220
221 #[cfg(not(target_os = "solana"))]
223 pub fn get_account_at_index(
224 &self,
225 index_in_transaction: IndexOfAccount,
226 ) -> Result<&RefCell<AccountSharedData>, InstructionError> {
227 self.accounts
228 .get(index_in_transaction)
229 .ok_or(InstructionError::NotEnoughAccountKeys)
230 }
231
232 pub fn find_index_of_account(&self, pubkey: &Pubkey) -> Option<IndexOfAccount> {
234 self.account_keys
235 .iter()
236 .position(|key| key == pubkey)
237 .map(|index| index as IndexOfAccount)
238 }
239
240 pub fn find_index_of_program_account(&self, pubkey: &Pubkey) -> Option<IndexOfAccount> {
242 self.account_keys
243 .iter()
244 .rposition(|key| key == pubkey)
245 .map(|index| index as IndexOfAccount)
246 }
247
248 pub fn get_instruction_trace_capacity(&self) -> usize {
250 self.instruction_trace_capacity
251 }
252
253 pub fn get_instruction_trace_length(&self) -> usize {
258 self.instruction_trace.len().saturating_sub(1)
259 }
260
261 pub fn get_instruction_context_at_index_in_trace(
263 &self,
264 index_in_trace: usize,
265 ) -> Result<&InstructionContext, InstructionError> {
266 self.instruction_trace
267 .get(index_in_trace)
268 .ok_or(InstructionError::CallDepth)
269 }
270
271 pub fn get_instruction_context_at_nesting_level(
273 &self,
274 nesting_level: usize,
275 ) -> Result<&InstructionContext, InstructionError> {
276 let index_in_trace = *self
277 .instruction_stack
278 .get(nesting_level)
279 .ok_or(InstructionError::CallDepth)?;
280 let instruction_context = self.get_instruction_context_at_index_in_trace(index_in_trace)?;
281 debug_assert_eq!(instruction_context.nesting_level, nesting_level);
282 Ok(instruction_context)
283 }
284
285 pub fn get_instruction_stack_capacity(&self) -> usize {
287 self.instruction_stack_capacity
288 }
289
290 pub fn get_instruction_context_stack_height(&self) -> usize {
293 self.instruction_stack.len()
294 }
295
296 pub fn get_current_instruction_context(&self) -> Result<&InstructionContext, InstructionError> {
298 let level = self
299 .get_instruction_context_stack_height()
300 .checked_sub(1)
301 .ok_or(InstructionError::CallDepth)?;
302 self.get_instruction_context_at_nesting_level(level)
303 }
304
305 pub fn get_next_instruction_context(
309 &mut self,
310 ) -> Result<&mut InstructionContext, InstructionError> {
311 self.instruction_trace
312 .last_mut()
313 .ok_or(InstructionError::CallDepth)
314 }
315
316 #[cfg(not(target_os = "solana"))]
318 pub fn push(&mut self) -> Result<(), InstructionError> {
319 let nesting_level = self.get_instruction_context_stack_height();
320 let caller_instruction_context = self
321 .instruction_trace
322 .last()
323 .ok_or(InstructionError::CallDepth)?;
324 let callee_instruction_accounts_lamport_sum =
325 self.instruction_accounts_lamport_sum(caller_instruction_context)?;
326 if !self.instruction_stack.is_empty() {
327 let caller_instruction_context = self.get_current_instruction_context()?;
328 let original_caller_instruction_accounts_lamport_sum =
329 caller_instruction_context.instruction_accounts_lamport_sum;
330 let current_caller_instruction_accounts_lamport_sum =
331 self.instruction_accounts_lamport_sum(caller_instruction_context)?;
332 if original_caller_instruction_accounts_lamport_sum
333 != current_caller_instruction_accounts_lamport_sum
334 {
335 return Err(InstructionError::UnbalancedInstruction);
336 }
337 }
338 {
339 let instruction_context = self.get_next_instruction_context()?;
340 instruction_context.nesting_level = nesting_level;
341 instruction_context.instruction_accounts_lamport_sum =
342 callee_instruction_accounts_lamport_sum;
343 }
344 let index_in_trace = self.get_instruction_trace_length();
345 if index_in_trace >= self.instruction_trace_capacity {
346 return Err(InstructionError::MaxInstructionTraceLengthExceeded);
347 }
348 self.instruction_trace.push(InstructionContext::default());
349 if nesting_level >= self.instruction_stack_capacity {
350 return Err(InstructionError::CallDepth);
351 }
352 self.instruction_stack.push(index_in_trace);
353 Ok(())
354 }
355
356 #[cfg(not(target_os = "solana"))]
358 pub fn pop(&mut self) -> Result<(), InstructionError> {
359 if self.instruction_stack.is_empty() {
360 return Err(InstructionError::CallDepth);
361 }
362 let detected_an_unbalanced_instruction =
364 self.get_current_instruction_context()
365 .and_then(|instruction_context| {
366 for account_index in instruction_context.program_accounts.iter() {
368 self.get_account_at_index(*account_index)?
369 .try_borrow_mut()
370 .map_err(|_| InstructionError::AccountBorrowOutstanding)?;
371 }
372 self.instruction_accounts_lamport_sum(instruction_context)
373 .map(|instruction_accounts_lamport_sum| {
374 instruction_context.instruction_accounts_lamport_sum
375 != instruction_accounts_lamport_sum
376 })
377 });
378 self.instruction_stack.pop();
380 if detected_an_unbalanced_instruction? {
381 Err(InstructionError::UnbalancedInstruction)
382 } else {
383 Ok(())
384 }
385 }
386
387 pub fn get_return_data(&self) -> (&Pubkey, &[u8]) {
389 (&self.return_data.program_id, &self.return_data.data)
390 }
391
392 pub fn set_return_data(
394 &mut self,
395 program_id: Pubkey,
396 data: Vec<u8>,
397 ) -> Result<(), InstructionError> {
398 self.return_data = TransactionReturnData { program_id, data };
399 Ok(())
400 }
401
402 #[cfg(not(target_os = "solana"))]
404 fn instruction_accounts_lamport_sum(
405 &self,
406 instruction_context: &InstructionContext,
407 ) -> Result<u128, InstructionError> {
408 let mut instruction_accounts_lamport_sum: u128 = 0;
409 for instruction_account_index in 0..instruction_context.get_number_of_instruction_accounts()
410 {
411 if instruction_context
412 .is_instruction_account_duplicate(instruction_account_index)?
413 .is_some()
414 {
415 continue; }
417 let index_in_transaction = instruction_context
418 .get_index_of_instruction_account_in_transaction(instruction_account_index)?;
419 instruction_accounts_lamport_sum = (self
420 .get_account_at_index(index_in_transaction)?
421 .try_borrow()
422 .map_err(|_| InstructionError::AccountBorrowOutstanding)?
423 .lamports() as u128)
424 .checked_add(instruction_accounts_lamport_sum)
425 .ok_or(InstructionError::ArithmeticOverflow)?;
426 }
427 Ok(instruction_accounts_lamport_sum)
428 }
429
430 pub fn accounts_resize_delta(&self) -> Result<i64, InstructionError> {
432 self.accounts_resize_delta
433 .try_borrow()
434 .map_err(|_| InstructionError::GenericError)
435 .map(|value_ref| *value_ref)
436 }
437}
438
439#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
441pub struct TransactionReturnData {
442 pub program_id: Pubkey,
443 pub data: Vec<u8>,
444}
445
446#[derive(Debug, Clone, Default, Eq, PartialEq)]
450pub struct InstructionContext {
451 nesting_level: usize,
452 instruction_accounts_lamport_sum: u128,
453 program_accounts: Vec<IndexOfAccount>,
454 instruction_accounts: Vec<InstructionAccount>,
455 instruction_data: Vec<u8>,
456}
457
458impl InstructionContext {
459 #[cfg(not(target_os = "solana"))]
461 pub fn configure(
462 &mut self,
463 program_accounts: &[IndexOfAccount],
464 instruction_accounts: &[InstructionAccount],
465 instruction_data: &[u8],
466 ) {
467 self.program_accounts = program_accounts.to_vec();
468 self.instruction_accounts = instruction_accounts.to_vec();
469 self.instruction_data = instruction_data.to_vec();
470 }
471
472 pub fn get_stack_height(&self) -> usize {
476 self.nesting_level.saturating_add(1)
477 }
478
479 pub fn get_number_of_program_accounts(&self) -> IndexOfAccount {
481 self.program_accounts.len() as IndexOfAccount
482 }
483
484 pub fn get_number_of_instruction_accounts(&self) -> IndexOfAccount {
486 self.instruction_accounts.len() as IndexOfAccount
487 }
488
489 pub fn check_number_of_instruction_accounts(
491 &self,
492 expected_at_least: IndexOfAccount,
493 ) -> Result<(), InstructionError> {
494 if self.get_number_of_instruction_accounts() < expected_at_least {
495 Err(InstructionError::NotEnoughAccountKeys)
496 } else {
497 Ok(())
498 }
499 }
500
501 pub fn get_instruction_data(&self) -> &[u8] {
503 &self.instruction_data
504 }
505
506 pub fn find_index_of_program_account(
508 &self,
509 transaction_context: &TransactionContext,
510 pubkey: &Pubkey,
511 ) -> Option<IndexOfAccount> {
512 self.program_accounts
513 .iter()
514 .position(|index_in_transaction| {
515 transaction_context
516 .account_keys
517 .get(*index_in_transaction as usize)
518 == Some(pubkey)
519 })
520 .map(|index| index as IndexOfAccount)
521 }
522
523 pub fn find_index_of_instruction_account(
525 &self,
526 transaction_context: &TransactionContext,
527 pubkey: &Pubkey,
528 ) -> Option<IndexOfAccount> {
529 self.instruction_accounts
530 .iter()
531 .position(|instruction_account| {
532 transaction_context
533 .account_keys
534 .get(instruction_account.index_in_transaction as usize)
535 == Some(pubkey)
536 })
537 .map(|index| index as IndexOfAccount)
538 }
539
540 pub fn get_index_of_program_account_in_transaction(
542 &self,
543 program_account_index: IndexOfAccount,
544 ) -> Result<IndexOfAccount, InstructionError> {
545 Ok(*self
546 .program_accounts
547 .get(program_account_index as usize)
548 .ok_or(InstructionError::NotEnoughAccountKeys)?)
549 }
550
551 pub fn get_index_of_instruction_account_in_transaction(
553 &self,
554 instruction_account_index: IndexOfAccount,
555 ) -> Result<IndexOfAccount, InstructionError> {
556 Ok(self
557 .instruction_accounts
558 .get(instruction_account_index as usize)
559 .ok_or(InstructionError::NotEnoughAccountKeys)?
560 .index_in_transaction as IndexOfAccount)
561 }
562
563 pub fn is_instruction_account_duplicate(
566 &self,
567 instruction_account_index: IndexOfAccount,
568 ) -> Result<Option<IndexOfAccount>, InstructionError> {
569 let index_in_callee = self
570 .instruction_accounts
571 .get(instruction_account_index as usize)
572 .ok_or(InstructionError::NotEnoughAccountKeys)?
573 .index_in_callee;
574 Ok(if index_in_callee == instruction_account_index {
575 None
576 } else {
577 Some(index_in_callee)
578 })
579 }
580
581 pub fn get_last_program_key<'a, 'b: 'a>(
583 &'a self,
584 transaction_context: &'b TransactionContext,
585 ) -> Result<&'b Pubkey, InstructionError> {
586 self.get_index_of_program_account_in_transaction(
587 self.get_number_of_program_accounts().saturating_sub(1),
588 )
589 .and_then(|index_in_transaction| {
590 transaction_context.get_key_of_account_at_index(index_in_transaction)
591 })
592 }
593
594 fn try_borrow_account<'a, 'b: 'a>(
595 &'a self,
596 transaction_context: &'b TransactionContext,
597 index_in_transaction: IndexOfAccount,
598 index_in_instruction: IndexOfAccount,
599 ) -> Result<BorrowedAccount<'a>, InstructionError> {
600 let account = transaction_context
601 .accounts
602 .get(index_in_transaction)
603 .ok_or(InstructionError::MissingAccount)?
604 .try_borrow_mut()
605 .map_err(|_| InstructionError::AccountBorrowFailed)?;
606 Ok(BorrowedAccount {
607 transaction_context,
608 instruction_context: self,
609 index_in_transaction,
610 index_in_instruction,
611 account,
612 })
613 }
614
615 pub fn try_borrow_last_program_account<'a, 'b: 'a>(
617 &'a self,
618 transaction_context: &'b TransactionContext,
619 ) -> Result<BorrowedAccount<'a>, InstructionError> {
620 let result = self.try_borrow_program_account(
621 transaction_context,
622 self.get_number_of_program_accounts().saturating_sub(1),
623 );
624 debug_assert!(result.is_ok());
625 result
626 }
627
628 pub fn try_borrow_program_account<'a, 'b: 'a>(
630 &'a self,
631 transaction_context: &'b TransactionContext,
632 program_account_index: IndexOfAccount,
633 ) -> Result<BorrowedAccount<'a>, InstructionError> {
634 let index_in_transaction =
635 self.get_index_of_program_account_in_transaction(program_account_index)?;
636 self.try_borrow_account(
637 transaction_context,
638 index_in_transaction,
639 program_account_index,
640 )
641 }
642
643 pub fn try_borrow_instruction_account<'a, 'b: 'a>(
645 &'a self,
646 transaction_context: &'b TransactionContext,
647 instruction_account_index: IndexOfAccount,
648 ) -> Result<BorrowedAccount<'a>, InstructionError> {
649 let index_in_transaction =
650 self.get_index_of_instruction_account_in_transaction(instruction_account_index)?;
651 self.try_borrow_account(
652 transaction_context,
653 index_in_transaction,
654 self.get_number_of_program_accounts()
655 .saturating_add(instruction_account_index),
656 )
657 }
658
659 pub fn is_instruction_account_signer(
661 &self,
662 instruction_account_index: IndexOfAccount,
663 ) -> Result<bool, InstructionError> {
664 Ok(self
665 .instruction_accounts
666 .get(instruction_account_index as usize)
667 .ok_or(InstructionError::MissingAccount)?
668 .is_signer)
669 }
670
671 pub fn is_instruction_account_writable(
673 &self,
674 instruction_account_index: IndexOfAccount,
675 ) -> Result<bool, InstructionError> {
676 Ok(self
677 .instruction_accounts
678 .get(instruction_account_index as usize)
679 .ok_or(InstructionError::MissingAccount)?
680 .is_writable)
681 }
682
683 pub fn get_signers(
685 &self,
686 transaction_context: &TransactionContext,
687 ) -> Result<HashSet<Pubkey>, InstructionError> {
688 let mut result = HashSet::new();
689 for instruction_account in self.instruction_accounts.iter() {
690 if instruction_account.is_signer {
691 result.insert(
692 *transaction_context
693 .get_key_of_account_at_index(instruction_account.index_in_transaction)?,
694 );
695 }
696 }
697 Ok(result)
698 }
699}
700
701#[derive(Debug)]
703pub struct BorrowedAccount<'a> {
704 transaction_context: &'a TransactionContext,
705 instruction_context: &'a InstructionContext,
706 index_in_transaction: IndexOfAccount,
707 index_in_instruction: IndexOfAccount,
708 account: RefMut<'a, AccountSharedData>,
709}
710
711impl<'a> BorrowedAccount<'a> {
712 pub fn transaction_context(&self) -> &TransactionContext {
714 self.transaction_context
715 }
716
717 #[inline]
719 pub fn get_index_in_transaction(&self) -> IndexOfAccount {
720 self.index_in_transaction
721 }
722
723 #[inline]
725 pub fn get_key(&self) -> &Pubkey {
726 self.transaction_context
727 .get_key_of_account_at_index(self.index_in_transaction)
728 .unwrap()
729 }
730
731 #[inline]
733 pub fn get_owner(&self) -> &Pubkey {
734 self.account.owner()
735 }
736
737 #[cfg(not(target_os = "solana"))]
739 pub fn set_owner(&mut self, pubkey: &[u8]) -> Result<(), InstructionError> {
740 if !self.is_owned_by_current_program() {
742 return Err(InstructionError::ModifiedProgramId);
743 }
744 if !self.is_writable() {
746 return Err(InstructionError::ModifiedProgramId);
747 }
748 if self.is_executable() {
750 return Err(InstructionError::ModifiedProgramId);
751 }
752 if !is_zeroed(self.get_data()) {
754 return Err(InstructionError::ModifiedProgramId);
755 }
756 if self.get_owner().to_bytes() == pubkey {
758 return Ok(());
759 }
760 self.touch()?;
761 self.account.copy_into_owner_from_slice(pubkey);
762 Ok(())
763 }
764
765 #[inline]
767 pub fn get_lamports(&self) -> u64 {
768 self.account.lamports()
769 }
770
771 #[cfg(not(target_os = "solana"))]
773 pub fn set_lamports(&mut self, lamports: u64) -> Result<(), InstructionError> {
774 if !self.is_owned_by_current_program() && lamports < self.get_lamports() {
776 return Err(InstructionError::ExternalAccountLamportSpend);
777 }
778 if !self.is_writable() {
780 return Err(InstructionError::ReadonlyLamportChange);
781 }
782 if self.is_executable() {
784 return Err(InstructionError::ExecutableLamportChange);
785 }
786 if self.get_lamports() == lamports {
788 return Ok(());
789 }
790 self.touch()?;
791 self.account.set_lamports(lamports);
792 Ok(())
793 }
794
795 #[cfg(not(target_os = "solana"))]
797 pub fn checked_add_lamports(&mut self, lamports: u64) -> Result<(), InstructionError> {
798 self.set_lamports(
799 self.get_lamports()
800 .checked_add(lamports)
801 .ok_or(InstructionError::ArithmeticOverflow)?,
802 )
803 }
804
805 #[cfg(not(target_os = "solana"))]
807 pub fn checked_sub_lamports(&mut self, lamports: u64) -> Result<(), InstructionError> {
808 self.set_lamports(
809 self.get_lamports()
810 .checked_sub(lamports)
811 .ok_or(InstructionError::ArithmeticOverflow)?,
812 )
813 }
814
815 #[inline]
817 pub fn get_data(&self) -> &[u8] {
818 self.account.data()
819 }
820
821 #[cfg(not(target_os = "solana"))]
823 pub fn get_data_mut(&mut self) -> Result<&mut [u8], InstructionError> {
824 self.can_data_be_changed()?;
825 self.touch()?;
826 self.make_data_mut();
827 Ok(self.account.data_as_mut_slice())
828 }
829
830 #[cfg(not(target_os = "solana"))]
835 pub fn spare_data_capacity_mut(&mut self) -> Result<&mut [MaybeUninit<u8>], InstructionError> {
836 debug_assert!(!self.account.is_shared());
837 Ok(self.account.spare_data_capacity_mut())
838 }
839
840 #[cfg(all(
846 not(target_os = "solana"),
847 any(test, feature = "dev-context-only-utils")
848 ))]
849 pub fn set_data(&mut self, data: Vec<u8>) -> Result<(), InstructionError> {
850 self.can_data_be_resized(data.len())?;
851 self.can_data_be_changed()?;
852 self.touch()?;
853
854 self.update_accounts_resize_delta(data.len())?;
855 self.account.set_data(data);
856 Ok(())
857 }
858
859 #[cfg(not(target_os = "solana"))]
864 pub fn set_data_from_slice(&mut self, data: &[u8]) -> Result<(), InstructionError> {
865 self.can_data_be_resized(data.len())?;
866 self.can_data_be_changed()?;
867 self.touch()?;
868 self.update_accounts_resize_delta(data.len())?;
869 self.account.set_data_from_slice(data);
874
875 Ok(())
876 }
877
878 #[cfg(not(target_os = "solana"))]
882 pub fn set_data_length(&mut self, new_length: usize) -> Result<(), InstructionError> {
883 self.can_data_be_resized(new_length)?;
884 self.can_data_be_changed()?;
885 if self.get_data().len() == new_length {
887 return Ok(());
888 }
889 self.touch()?;
890 self.update_accounts_resize_delta(new_length)?;
891 self.account.resize(new_length, 0);
892 Ok(())
893 }
894
895 #[cfg(not(target_os = "solana"))]
897 pub fn extend_from_slice(&mut self, data: &[u8]) -> Result<(), InstructionError> {
898 let new_len = self.get_data().len().saturating_add(data.len());
899 self.can_data_be_resized(new_len)?;
900 self.can_data_be_changed()?;
901
902 if data.is_empty() {
903 return Ok(());
904 }
905
906 self.touch()?;
907 self.update_accounts_resize_delta(new_len)?;
908 self.make_data_mut();
912 self.account.extend_from_slice(data);
913 Ok(())
914 }
915
916 #[cfg(not(target_os = "solana"))]
919 pub fn reserve(&mut self, additional: usize) -> Result<(), InstructionError> {
920 self.make_data_mut();
925 self.account.reserve(additional);
926
927 Ok(())
928 }
929
930 #[cfg(not(target_os = "solana"))]
932 pub fn capacity(&self) -> usize {
933 self.account.capacity()
934 }
935
936 #[cfg(not(target_os = "solana"))]
944 pub fn is_shared(&self) -> bool {
945 self.account.is_shared()
946 }
947
948 #[cfg(not(target_os = "solana"))]
949 fn make_data_mut(&mut self) {
950 if self.account.is_shared() {
959 self.account.reserve(MAX_PERMITTED_DATA_INCREASE);
960 }
961 }
962
963 #[cfg(not(target_os = "solana"))]
965 pub fn get_state<T: serde::de::DeserializeOwned>(&self) -> Result<T, InstructionError> {
966 self.account
967 .deserialize_data()
968 .map_err(|_| InstructionError::InvalidAccountData)
969 }
970
971 #[cfg(not(target_os = "solana"))]
973 pub fn set_state<T: serde::Serialize>(&mut self, state: &T) -> Result<(), InstructionError> {
974 let data = self.get_data_mut()?;
975 let serialized_size =
976 bincode::serialized_size(state).map_err(|_| InstructionError::GenericError)?;
977 if serialized_size > data.len() as u64 {
978 return Err(InstructionError::AccountDataTooSmall);
979 }
980 bincode::serialize_into(&mut *data, state).map_err(|_| InstructionError::GenericError)?;
981 Ok(())
982 }
983
984 #[cfg(not(target_os = "solana"))]
987 pub fn is_rent_exempt_at_data_length(&self, data_length: usize) -> bool {
988 self.transaction_context
989 .rent
990 .is_exempt(self.get_lamports(), data_length)
991 }
992
993 #[inline]
995 pub fn is_executable(&self) -> bool {
996 self.account.executable()
997 }
998
999 #[cfg(not(target_os = "solana"))]
1001 pub fn set_executable(&mut self, is_executable: bool) -> Result<(), InstructionError> {
1002 if !self
1004 .transaction_context
1005 .rent
1006 .is_exempt(self.get_lamports(), self.get_data().len())
1007 {
1008 return Err(InstructionError::ExecutableAccountNotRentExempt);
1009 }
1010 if !self.is_owned_by_current_program() {
1012 return Err(InstructionError::ExecutableModified);
1013 }
1014 if !self.is_writable() {
1016 return Err(InstructionError::ExecutableModified);
1017 }
1018 if self.is_executable() && !is_executable {
1020 return Err(InstructionError::ExecutableModified);
1021 }
1022 if self.is_executable() == is_executable {
1024 return Ok(());
1025 }
1026 self.touch()?;
1027 self.account.set_executable(is_executable);
1028 Ok(())
1029 }
1030
1031 #[cfg(not(target_os = "solana"))]
1033 #[inline]
1034 pub fn get_rent_epoch(&self) -> u64 {
1035 self.account.rent_epoch()
1036 }
1037
1038 pub fn is_signer(&self) -> bool {
1040 if self.index_in_instruction < self.instruction_context.get_number_of_program_accounts() {
1041 return false;
1042 }
1043 self.instruction_context
1044 .is_instruction_account_signer(
1045 self.index_in_instruction
1046 .saturating_sub(self.instruction_context.get_number_of_program_accounts()),
1047 )
1048 .unwrap_or_default()
1049 }
1050
1051 pub fn is_writable(&self) -> bool {
1053 if self.index_in_instruction < self.instruction_context.get_number_of_program_accounts() {
1054 return false;
1055 }
1056 self.instruction_context
1057 .is_instruction_account_writable(
1058 self.index_in_instruction
1059 .saturating_sub(self.instruction_context.get_number_of_program_accounts()),
1060 )
1061 .unwrap_or_default()
1062 }
1063
1064 pub fn is_owned_by_current_program(&self) -> bool {
1066 self.instruction_context
1067 .get_last_program_key(self.transaction_context)
1068 .map(|key| key == self.get_owner())
1069 .unwrap_or_default()
1070 }
1071
1072 #[cfg(not(target_os = "solana"))]
1074 pub fn can_data_be_changed(&self) -> Result<(), InstructionError> {
1075 if self.is_executable() {
1077 return Err(InstructionError::ExecutableDataModified);
1078 }
1079 if !self.is_writable() {
1081 return Err(InstructionError::ReadonlyDataModified);
1082 }
1083 if !self.is_owned_by_current_program() {
1085 return Err(InstructionError::ExternalAccountDataModified);
1086 }
1087 Ok(())
1088 }
1089
1090 #[cfg(not(target_os = "solana"))]
1092 pub fn can_data_be_resized(&self, new_length: usize) -> Result<(), InstructionError> {
1093 let old_length = self.get_data().len();
1094 if new_length != old_length && !self.is_owned_by_current_program() {
1096 return Err(InstructionError::AccountDataSizeChanged);
1097 }
1098 if new_length > MAX_PERMITTED_DATA_LENGTH as usize {
1100 return Err(InstructionError::InvalidRealloc);
1101 }
1102 let length_delta = (new_length as i64).saturating_sub(old_length as i64);
1104 if self
1105 .transaction_context
1106 .accounts_resize_delta()?
1107 .saturating_add(length_delta)
1108 > MAX_PERMITTED_ACCOUNTS_DATA_ALLOCATIONS_PER_TRANSACTION
1109 {
1110 return Err(InstructionError::MaxAccountsDataAllocationsExceeded);
1111 }
1112 Ok(())
1113 }
1114
1115 #[cfg(not(target_os = "solana"))]
1116 fn touch(&self) -> Result<(), InstructionError> {
1117 self.transaction_context
1118 .accounts()
1119 .touch(self.index_in_transaction)
1120 }
1121
1122 #[cfg(not(target_os = "solana"))]
1123 fn update_accounts_resize_delta(&mut self, new_len: usize) -> Result<(), InstructionError> {
1124 let mut accounts_resize_delta = self
1125 .transaction_context
1126 .accounts_resize_delta
1127 .try_borrow_mut()
1128 .map_err(|_| InstructionError::GenericError)?;
1129 *accounts_resize_delta = accounts_resize_delta
1130 .saturating_add((new_len as i64).saturating_sub(self.get_data().len() as i64));
1131 Ok(())
1132 }
1133}
1134
1135#[cfg(not(target_os = "solana"))]
1137pub struct ExecutionRecord {
1138 pub accounts: Vec<TransactionAccount>,
1139 pub return_data: TransactionReturnData,
1140 pub touched_account_count: u64,
1141 pub accounts_resize_delta: i64,
1142}
1143
1144#[cfg(not(target_os = "solana"))]
1146impl From<TransactionContext> for ExecutionRecord {
1147 fn from(context: TransactionContext) -> Self {
1148 let accounts = Rc::try_unwrap(context.accounts)
1149 .expect("transaction_context.accounts has unexpected outstanding refs");
1150 let touched_account_count = accounts.touched_count() as u64;
1151 let accounts = accounts.into_accounts();
1152 Self {
1153 accounts: Vec::from(Pin::into_inner(context.account_keys))
1154 .into_iter()
1155 .zip(accounts)
1156 .collect(),
1157 return_data: context.return_data,
1158 touched_account_count,
1159 accounts_resize_delta: RefCell::into_inner(context.accounts_resize_delta),
1160 }
1161 }
1162}
1163
1164#[cfg(not(target_os = "solana"))]
1165fn is_zeroed(buf: &[u8]) -> bool {
1166 const ZEROS_LEN: usize = 1024;
1167 const ZEROS: [u8; ZEROS_LEN] = [0; ZEROS_LEN];
1168 let mut chunks = buf.chunks_exact(ZEROS_LEN);
1169
1170 #[allow(clippy::indexing_slicing)]
1171 {
1172 chunks.all(|chunk| chunk == &ZEROS[..])
1173 && chunks.remainder() == &ZEROS[..chunks.remainder().len()]
1174 }
1175}