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 clone_solana_signature::Signature;
11#[cfg(not(target_os = "solana"))]
12use {clone_solana_account::WritableAccount, clone_solana_rent::Rent, std::mem::MaybeUninit};
13use {
14 clone_solana_account::{AccountSharedData, ReadableAccount},
15 clone_solana_instruction::error::InstructionError,
16 clone_solana_instructions_sysvar as instructions,
17 clone_solana_pubkey::Pubkey,
18 std::{
19 cell::{Ref, RefCell, RefMut},
20 collections::HashSet,
21 pin::Pin,
22 rc::Rc,
23 },
24};
25
26#[cfg(not(target_os = "solana"))]
28const MAX_PERMITTED_DATA_LENGTH: u64 = 10 * 1024 * 1024;
29#[cfg(test)]
30static_assertions::const_assert_eq!(
31 MAX_PERMITTED_DATA_LENGTH,
32 clone_solana_system_interface::MAX_PERMITTED_DATA_LENGTH
33);
34
35#[cfg(not(target_os = "solana"))]
37const MAX_PERMITTED_ACCOUNTS_DATA_ALLOCATIONS_PER_TRANSACTION: i64 =
38 MAX_PERMITTED_DATA_LENGTH as i64 * 2;
39#[cfg(test)]
40static_assertions::const_assert_eq!(
41 MAX_PERMITTED_ACCOUNTS_DATA_ALLOCATIONS_PER_TRANSACTION,
42 clone_solana_system_interface::MAX_PERMITTED_ACCOUNTS_DATA_ALLOCATIONS_PER_TRANSACTION
43);
44
45#[cfg(not(target_os = "solana"))]
47const MAX_PERMITTED_DATA_INCREASE: usize = 1_024 * 10;
48#[cfg(test)]
49static_assertions::const_assert_eq!(
50 MAX_PERMITTED_DATA_INCREASE,
51 clone_solana_account_info::MAX_PERMITTED_DATA_INCREASE
52);
53
54pub type IndexOfAccount = u16;
56
57#[derive(Clone, Debug, Eq, PartialEq)]
61pub struct InstructionAccount {
62 pub index_in_transaction: IndexOfAccount,
64 pub index_in_caller: IndexOfAccount,
68 pub index_in_callee: IndexOfAccount,
72 pub is_signer: bool,
74 pub is_writable: bool,
76}
77
78pub type TransactionAccount = (Pubkey, AccountSharedData);
80
81#[derive(Clone, Debug, PartialEq)]
82pub struct TransactionAccounts {
83 accounts: Vec<RefCell<AccountSharedData>>,
84 touched_flags: RefCell<Box<[bool]>>,
85}
86
87impl TransactionAccounts {
88 #[cfg(not(target_os = "solana"))]
89 fn new(accounts: Vec<RefCell<AccountSharedData>>) -> TransactionAccounts {
90 TransactionAccounts {
91 touched_flags: RefCell::new(vec![false; accounts.len()].into_boxed_slice()),
92 accounts,
93 }
94 }
95
96 fn len(&self) -> usize {
97 self.accounts.len()
98 }
99
100 fn get(&self, index: IndexOfAccount) -> Option<&RefCell<AccountSharedData>> {
101 self.accounts.get(index as usize)
102 }
103
104 #[cfg(not(target_os = "solana"))]
105 pub fn touch(&self, index: IndexOfAccount) -> Result<(), InstructionError> {
106 *self
107 .touched_flags
108 .borrow_mut()
109 .get_mut(index as usize)
110 .ok_or(InstructionError::NotEnoughAccountKeys)? = true;
111 Ok(())
112 }
113
114 #[cfg(not(target_os = "solana"))]
115 pub fn touched_count(&self) -> usize {
116 self.touched_flags
117 .borrow()
118 .iter()
119 .fold(0usize, |accumulator, was_touched| {
120 accumulator.saturating_add(*was_touched as usize)
121 })
122 }
123
124 pub fn try_borrow(
125 &self,
126 index: IndexOfAccount,
127 ) -> Result<Ref<'_, AccountSharedData>, InstructionError> {
128 self.accounts
129 .get(index as usize)
130 .ok_or(InstructionError::MissingAccount)?
131 .try_borrow()
132 .map_err(|_| InstructionError::AccountBorrowFailed)
133 }
134
135 pub fn into_accounts(self) -> Vec<AccountSharedData> {
136 self.accounts
137 .into_iter()
138 .map(|account| account.into_inner())
139 .collect()
140 }
141}
142
143#[derive(Debug, Clone, PartialEq)]
147pub struct TransactionContext {
148 account_keys: Pin<Box<[Pubkey]>>,
149 accounts: Rc<TransactionAccounts>,
150 instruction_stack_capacity: usize,
151 instruction_trace_capacity: usize,
152 instruction_stack: Vec<usize>,
153 instruction_trace: Vec<InstructionContext>,
154 top_level_instruction_index: usize,
155 return_data: TransactionReturnData,
156 accounts_resize_delta: RefCell<i64>,
157 #[cfg(not(target_os = "solana"))]
158 remove_accounts_executable_flag_checks: bool,
159 #[cfg(not(target_os = "solana"))]
160 rent: Rent,
161 #[cfg(all(
163 not(target_os = "solana"),
164 feature = "debug-signature",
165 debug_assertions
166 ))]
167 signature: Signature,
168}
169
170impl TransactionContext {
171 #[cfg(not(target_os = "solana"))]
173 pub fn new(
174 transaction_accounts: Vec<TransactionAccount>,
175 rent: Rent,
176 instruction_stack_capacity: usize,
177 instruction_trace_capacity: usize,
178 ) -> Self {
179 let (account_keys, accounts): (Vec<_>, Vec<_>) = transaction_accounts
180 .into_iter()
181 .map(|(key, account)| (key, RefCell::new(account)))
182 .unzip();
183 Self {
184 account_keys: Pin::new(account_keys.into_boxed_slice()),
185 accounts: Rc::new(TransactionAccounts::new(accounts)),
186 instruction_stack_capacity,
187 instruction_trace_capacity,
188 instruction_stack: Vec::with_capacity(instruction_stack_capacity),
189 instruction_trace: vec![InstructionContext::default()],
190 top_level_instruction_index: 0,
191 return_data: TransactionReturnData::default(),
192 accounts_resize_delta: RefCell::new(0),
193 remove_accounts_executable_flag_checks: true,
194 rent,
195 #[cfg(all(
196 not(target_os = "solana"),
197 feature = "debug-signature",
198 debug_assertions
199 ))]
200 signature: Signature::default(),
201 }
202 }
203
204 #[cfg(not(target_os = "solana"))]
205 pub fn set_remove_accounts_executable_flag_checks(&mut self, enabled: bool) {
206 self.remove_accounts_executable_flag_checks = enabled;
207 }
208
209 #[cfg(not(target_os = "solana"))]
211 pub fn deconstruct_without_keys(self) -> Result<Vec<AccountSharedData>, InstructionError> {
212 if !self.instruction_stack.is_empty() {
213 return Err(InstructionError::CallDepth);
214 }
215
216 Ok(Rc::try_unwrap(self.accounts)
217 .expect("transaction_context.accounts has unexpected outstanding refs")
218 .into_accounts())
219 }
220
221 #[cfg(not(target_os = "solana"))]
222 pub fn accounts(&self) -> &Rc<TransactionAccounts> {
223 &self.accounts
224 }
225
226 #[cfg(all(
228 not(target_os = "solana"),
229 feature = "debug-signature",
230 debug_assertions
231 ))]
232 pub fn set_signature(&mut self, signature: &Signature) {
233 self.signature = *signature;
234 }
235
236 #[cfg(all(
238 not(target_os = "solana"),
239 feature = "debug-signature",
240 debug_assertions
241 ))]
242 pub fn get_signature(&self) -> &Signature {
243 &self.signature
244 }
245
246 pub fn get_number_of_accounts(&self) -> IndexOfAccount {
248 self.accounts.len() as IndexOfAccount
249 }
250
251 pub fn get_key_of_account_at_index(
253 &self,
254 index_in_transaction: IndexOfAccount,
255 ) -> Result<&Pubkey, InstructionError> {
256 self.account_keys
257 .get(index_in_transaction as usize)
258 .ok_or(InstructionError::NotEnoughAccountKeys)
259 }
260
261 #[cfg(all(
263 not(target_os = "solana"),
264 any(test, feature = "dev-context-only-utils")
265 ))]
266 pub fn get_account_at_index(
267 &self,
268 index_in_transaction: IndexOfAccount,
269 ) -> Result<&RefCell<AccountSharedData>, InstructionError> {
270 self.accounts
271 .get(index_in_transaction)
272 .ok_or(InstructionError::NotEnoughAccountKeys)
273 }
274
275 pub fn find_index_of_account(&self, pubkey: &Pubkey) -> Option<IndexOfAccount> {
277 self.account_keys
278 .iter()
279 .position(|key| key == pubkey)
280 .map(|index| index as IndexOfAccount)
281 }
282
283 pub fn find_index_of_program_account(&self, pubkey: &Pubkey) -> Option<IndexOfAccount> {
285 self.account_keys
286 .iter()
287 .rposition(|key| key == pubkey)
288 .map(|index| index as IndexOfAccount)
289 }
290
291 pub fn get_instruction_trace_capacity(&self) -> usize {
293 self.instruction_trace_capacity
294 }
295
296 pub fn get_instruction_trace_length(&self) -> usize {
301 self.instruction_trace.len().saturating_sub(1)
302 }
303
304 pub fn get_instruction_context_at_index_in_trace(
306 &self,
307 index_in_trace: usize,
308 ) -> Result<&InstructionContext, InstructionError> {
309 self.instruction_trace
310 .get(index_in_trace)
311 .ok_or(InstructionError::CallDepth)
312 }
313
314 pub fn get_instruction_context_at_nesting_level(
316 &self,
317 nesting_level: usize,
318 ) -> Result<&InstructionContext, InstructionError> {
319 let index_in_trace = *self
320 .instruction_stack
321 .get(nesting_level)
322 .ok_or(InstructionError::CallDepth)?;
323 let instruction_context = self.get_instruction_context_at_index_in_trace(index_in_trace)?;
324 debug_assert_eq!(instruction_context.nesting_level, nesting_level);
325 Ok(instruction_context)
326 }
327
328 pub fn get_instruction_stack_capacity(&self) -> usize {
330 self.instruction_stack_capacity
331 }
332
333 pub fn get_instruction_context_stack_height(&self) -> usize {
336 self.instruction_stack.len()
337 }
338
339 pub fn get_current_instruction_context(&self) -> Result<&InstructionContext, InstructionError> {
341 let level = self
342 .get_instruction_context_stack_height()
343 .checked_sub(1)
344 .ok_or(InstructionError::CallDepth)?;
345 self.get_instruction_context_at_nesting_level(level)
346 }
347
348 pub fn get_next_instruction_context(
352 &mut self,
353 ) -> Result<&mut InstructionContext, InstructionError> {
354 self.instruction_trace
355 .last_mut()
356 .ok_or(InstructionError::CallDepth)
357 }
358
359 #[cfg(not(target_os = "solana"))]
361 pub fn push(&mut self) -> Result<(), InstructionError> {
362 let nesting_level = self.get_instruction_context_stack_height();
363 let caller_instruction_context = self
364 .instruction_trace
365 .last()
366 .ok_or(InstructionError::CallDepth)?;
367 let callee_instruction_accounts_lamport_sum =
368 self.instruction_accounts_lamport_sum(caller_instruction_context)?;
369 if !self.instruction_stack.is_empty() {
370 let caller_instruction_context = self.get_current_instruction_context()?;
371 let original_caller_instruction_accounts_lamport_sum =
372 caller_instruction_context.instruction_accounts_lamport_sum;
373 let current_caller_instruction_accounts_lamport_sum =
374 self.instruction_accounts_lamport_sum(caller_instruction_context)?;
375 if original_caller_instruction_accounts_lamport_sum
376 != current_caller_instruction_accounts_lamport_sum
377 {
378 return Err(InstructionError::UnbalancedInstruction);
379 }
380 }
381 {
382 let instruction_context = self.get_next_instruction_context()?;
383 instruction_context.nesting_level = nesting_level;
384 instruction_context.instruction_accounts_lamport_sum =
385 callee_instruction_accounts_lamport_sum;
386 }
387 let index_in_trace = self.get_instruction_trace_length();
388 if index_in_trace >= self.instruction_trace_capacity {
389 return Err(InstructionError::MaxInstructionTraceLengthExceeded);
390 }
391 self.instruction_trace.push(InstructionContext::default());
392 if nesting_level >= self.instruction_stack_capacity {
393 return Err(InstructionError::CallDepth);
394 }
395 self.instruction_stack.push(index_in_trace);
396 if let Some(index_in_transaction) = self.find_index_of_account(&instructions::id()) {
397 let mut mut_account_ref = self
398 .accounts
399 .get(index_in_transaction)
400 .ok_or(InstructionError::NotEnoughAccountKeys)?
401 .try_borrow_mut()
402 .map_err(|_| InstructionError::AccountBorrowFailed)?;
403 instructions::store_current_index(
404 mut_account_ref.data_as_mut_slice(),
405 self.top_level_instruction_index as u16,
406 );
407 }
408 Ok(())
409 }
410
411 #[cfg(not(target_os = "solana"))]
413 pub fn pop(&mut self) -> Result<(), InstructionError> {
414 if self.instruction_stack.is_empty() {
415 return Err(InstructionError::CallDepth);
416 }
417 let detected_an_unbalanced_instruction =
419 self.get_current_instruction_context()
420 .and_then(|instruction_context| {
421 for index_in_transaction in instruction_context.program_accounts.iter() {
423 self.accounts
424 .get(*index_in_transaction)
425 .ok_or(InstructionError::NotEnoughAccountKeys)?
426 .try_borrow_mut()
427 .map_err(|_| InstructionError::AccountBorrowOutstanding)?;
428 }
429 self.instruction_accounts_lamport_sum(instruction_context)
430 .map(|instruction_accounts_lamport_sum| {
431 instruction_context.instruction_accounts_lamport_sum
432 != instruction_accounts_lamport_sum
433 })
434 });
435 self.instruction_stack.pop();
437 if self.instruction_stack.is_empty() {
438 self.top_level_instruction_index = self.top_level_instruction_index.saturating_add(1);
439 }
440 if detected_an_unbalanced_instruction? {
441 Err(InstructionError::UnbalancedInstruction)
442 } else {
443 Ok(())
444 }
445 }
446
447 pub fn get_return_data(&self) -> (&Pubkey, &[u8]) {
449 (&self.return_data.program_id, &self.return_data.data)
450 }
451
452 pub fn set_return_data(
454 &mut self,
455 program_id: Pubkey,
456 data: Vec<u8>,
457 ) -> Result<(), InstructionError> {
458 self.return_data = TransactionReturnData { program_id, data };
459 Ok(())
460 }
461
462 #[cfg(not(target_os = "solana"))]
464 fn instruction_accounts_lamport_sum(
465 &self,
466 instruction_context: &InstructionContext,
467 ) -> Result<u128, InstructionError> {
468 let mut instruction_accounts_lamport_sum: u128 = 0;
469 for instruction_account_index in 0..instruction_context.get_number_of_instruction_accounts()
470 {
471 if instruction_context
472 .is_instruction_account_duplicate(instruction_account_index)?
473 .is_some()
474 {
475 continue; }
477 let index_in_transaction = instruction_context
478 .get_index_of_instruction_account_in_transaction(instruction_account_index)?;
479 instruction_accounts_lamport_sum = (self
480 .accounts
481 .get(index_in_transaction)
482 .ok_or(InstructionError::NotEnoughAccountKeys)?
483 .try_borrow()
484 .map_err(|_| InstructionError::AccountBorrowOutstanding)?
485 .lamports() as u128)
486 .checked_add(instruction_accounts_lamport_sum)
487 .ok_or(InstructionError::ArithmeticOverflow)?;
488 }
489 Ok(instruction_accounts_lamport_sum)
490 }
491
492 pub fn accounts_resize_delta(&self) -> Result<i64, InstructionError> {
494 self.accounts_resize_delta
495 .try_borrow()
496 .map_err(|_| InstructionError::GenericError)
497 .map(|value_ref| *value_ref)
498 }
499
500 pub fn account_data_write_access_handler(&self) -> Box<dyn Fn(u64) -> Result<u64, ()>> {
502 let accounts = Rc::clone(&self.accounts);
503 Box::new(move |index_in_transaction| {
504 let mut account = accounts
508 .accounts
509 .get(index_in_transaction as usize)
510 .ok_or(())?
511 .try_borrow_mut()
512 .map_err(|_| ())?;
513 accounts
514 .touch(index_in_transaction as IndexOfAccount)
515 .map_err(|_| ())?;
516
517 if account.is_shared() {
518 account.reserve(MAX_PERMITTED_DATA_INCREASE);
521 }
522 Ok(account.data_as_mut_slice().as_mut_ptr() as u64)
523 })
524 }
525}
526
527#[cfg_attr(
529 feature = "serde",
530 derive(serde_derive::Deserialize, serde_derive::Serialize)
531)]
532#[derive(Clone, Debug, Default, PartialEq, Eq)]
533pub struct TransactionReturnData {
534 pub program_id: Pubkey,
535 pub data: Vec<u8>,
536}
537
538#[derive(Debug, Clone, Default, Eq, PartialEq)]
542pub struct InstructionContext {
543 nesting_level: usize,
544 instruction_accounts_lamport_sum: u128,
545 program_accounts: Vec<IndexOfAccount>,
546 instruction_accounts: Vec<InstructionAccount>,
547 instruction_data: Vec<u8>,
548}
549
550impl InstructionContext {
551 #[cfg(not(target_os = "solana"))]
553 pub fn configure(
554 &mut self,
555 program_accounts: &[IndexOfAccount],
556 instruction_accounts: &[InstructionAccount],
557 instruction_data: &[u8],
558 ) {
559 self.program_accounts = program_accounts.to_vec();
560 self.instruction_accounts = instruction_accounts.to_vec();
561 self.instruction_data = instruction_data.to_vec();
562 }
563
564 pub fn get_stack_height(&self) -> usize {
568 self.nesting_level.saturating_add(1)
569 }
570
571 pub fn get_number_of_program_accounts(&self) -> IndexOfAccount {
573 self.program_accounts.len() as IndexOfAccount
574 }
575
576 pub fn get_number_of_instruction_accounts(&self) -> IndexOfAccount {
578 self.instruction_accounts.len() as IndexOfAccount
579 }
580
581 pub fn check_number_of_instruction_accounts(
583 &self,
584 expected_at_least: IndexOfAccount,
585 ) -> Result<(), InstructionError> {
586 if self.get_number_of_instruction_accounts() < expected_at_least {
587 Err(InstructionError::NotEnoughAccountKeys)
588 } else {
589 Ok(())
590 }
591 }
592
593 pub fn get_instruction_data(&self) -> &[u8] {
595 &self.instruction_data
596 }
597
598 pub fn find_index_of_program_account(
600 &self,
601 transaction_context: &TransactionContext,
602 pubkey: &Pubkey,
603 ) -> Option<IndexOfAccount> {
604 self.program_accounts
605 .iter()
606 .position(|index_in_transaction| {
607 transaction_context
608 .account_keys
609 .get(*index_in_transaction as usize)
610 == Some(pubkey)
611 })
612 .map(|index| index as IndexOfAccount)
613 }
614
615 pub fn find_index_of_instruction_account(
617 &self,
618 transaction_context: &TransactionContext,
619 pubkey: &Pubkey,
620 ) -> Option<IndexOfAccount> {
621 self.instruction_accounts
622 .iter()
623 .position(|instruction_account| {
624 transaction_context
625 .account_keys
626 .get(instruction_account.index_in_transaction as usize)
627 == Some(pubkey)
628 })
629 .map(|index| index as IndexOfAccount)
630 }
631
632 pub fn get_index_of_program_account_in_transaction(
634 &self,
635 program_account_index: IndexOfAccount,
636 ) -> Result<IndexOfAccount, InstructionError> {
637 Ok(*self
638 .program_accounts
639 .get(program_account_index as usize)
640 .ok_or(InstructionError::NotEnoughAccountKeys)?)
641 }
642
643 pub fn get_index_of_instruction_account_in_transaction(
645 &self,
646 instruction_account_index: IndexOfAccount,
647 ) -> Result<IndexOfAccount, InstructionError> {
648 Ok(self
649 .instruction_accounts
650 .get(instruction_account_index as usize)
651 .ok_or(InstructionError::NotEnoughAccountKeys)?
652 .index_in_transaction as IndexOfAccount)
653 }
654
655 pub fn is_instruction_account_duplicate(
658 &self,
659 instruction_account_index: IndexOfAccount,
660 ) -> Result<Option<IndexOfAccount>, InstructionError> {
661 let index_in_callee = self
662 .instruction_accounts
663 .get(instruction_account_index as usize)
664 .ok_or(InstructionError::NotEnoughAccountKeys)?
665 .index_in_callee;
666 Ok(if index_in_callee == instruction_account_index {
667 None
668 } else {
669 Some(index_in_callee)
670 })
671 }
672
673 pub fn get_last_program_key<'a, 'b: 'a>(
675 &'a self,
676 transaction_context: &'b TransactionContext,
677 ) -> Result<&'b Pubkey, InstructionError> {
678 self.get_index_of_program_account_in_transaction(
679 self.get_number_of_program_accounts().saturating_sub(1),
680 )
681 .and_then(|index_in_transaction| {
682 transaction_context.get_key_of_account_at_index(index_in_transaction)
683 })
684 }
685
686 fn try_borrow_account<'a, 'b: 'a>(
687 &'a self,
688 transaction_context: &'b TransactionContext,
689 index_in_transaction: IndexOfAccount,
690 index_in_instruction: IndexOfAccount,
691 ) -> Result<BorrowedAccount<'a>, InstructionError> {
692 let account = transaction_context
693 .accounts
694 .get(index_in_transaction)
695 .ok_or(InstructionError::MissingAccount)?
696 .try_borrow_mut()
697 .map_err(|_| InstructionError::AccountBorrowFailed)?;
698 Ok(BorrowedAccount {
699 transaction_context,
700 instruction_context: self,
701 index_in_transaction,
702 index_in_instruction,
703 account,
704 })
705 }
706
707 pub fn try_borrow_last_program_account<'a, 'b: 'a>(
709 &'a self,
710 transaction_context: &'b TransactionContext,
711 ) -> Result<BorrowedAccount<'a>, InstructionError> {
712 let result = self.try_borrow_program_account(
713 transaction_context,
714 self.get_number_of_program_accounts().saturating_sub(1),
715 );
716 debug_assert!(result.is_ok());
717 result
718 }
719
720 pub fn try_borrow_program_account<'a, 'b: 'a>(
722 &'a self,
723 transaction_context: &'b TransactionContext,
724 program_account_index: IndexOfAccount,
725 ) -> Result<BorrowedAccount<'a>, InstructionError> {
726 let index_in_transaction =
727 self.get_index_of_program_account_in_transaction(program_account_index)?;
728 self.try_borrow_account(
729 transaction_context,
730 index_in_transaction,
731 program_account_index,
732 )
733 }
734
735 pub fn try_borrow_instruction_account<'a, 'b: 'a>(
737 &'a self,
738 transaction_context: &'b TransactionContext,
739 instruction_account_index: IndexOfAccount,
740 ) -> Result<BorrowedAccount<'a>, InstructionError> {
741 let index_in_transaction =
742 self.get_index_of_instruction_account_in_transaction(instruction_account_index)?;
743 self.try_borrow_account(
744 transaction_context,
745 index_in_transaction,
746 self.get_number_of_program_accounts()
747 .saturating_add(instruction_account_index),
748 )
749 }
750
751 pub fn is_instruction_account_signer(
753 &self,
754 instruction_account_index: IndexOfAccount,
755 ) -> Result<bool, InstructionError> {
756 Ok(self
757 .instruction_accounts
758 .get(instruction_account_index as usize)
759 .ok_or(InstructionError::MissingAccount)?
760 .is_signer)
761 }
762
763 pub fn is_instruction_account_writable(
765 &self,
766 instruction_account_index: IndexOfAccount,
767 ) -> Result<bool, InstructionError> {
768 Ok(self
769 .instruction_accounts
770 .get(instruction_account_index as usize)
771 .ok_or(InstructionError::MissingAccount)?
772 .is_writable)
773 }
774
775 pub fn get_signers(
777 &self,
778 transaction_context: &TransactionContext,
779 ) -> Result<HashSet<Pubkey>, InstructionError> {
780 let mut result = HashSet::new();
781 for instruction_account in self.instruction_accounts.iter() {
782 if instruction_account.is_signer {
783 result.insert(
784 *transaction_context
785 .get_key_of_account_at_index(instruction_account.index_in_transaction)?,
786 );
787 }
788 }
789 Ok(result)
790 }
791}
792
793#[derive(Debug)]
795pub struct BorrowedAccount<'a> {
796 transaction_context: &'a TransactionContext,
797 instruction_context: &'a InstructionContext,
798 index_in_transaction: IndexOfAccount,
799 index_in_instruction: IndexOfAccount,
800 account: RefMut<'a, AccountSharedData>,
801}
802
803impl BorrowedAccount<'_> {
804 pub fn transaction_context(&self) -> &TransactionContext {
806 self.transaction_context
807 }
808
809 #[inline]
811 pub fn get_index_in_transaction(&self) -> IndexOfAccount {
812 self.index_in_transaction
813 }
814
815 #[inline]
817 pub fn get_key(&self) -> &Pubkey {
818 self.transaction_context
819 .get_key_of_account_at_index(self.index_in_transaction)
820 .unwrap()
821 }
822
823 #[inline]
825 pub fn get_owner(&self) -> &Pubkey {
826 self.account.owner()
827 }
828
829 #[cfg(not(target_os = "solana"))]
831 pub fn set_owner(&mut self, pubkey: &[u8]) -> Result<(), InstructionError> {
832 if !self.is_owned_by_current_program() {
834 return Err(InstructionError::ModifiedProgramId);
835 }
836 if !self.is_writable() {
838 return Err(InstructionError::ModifiedProgramId);
839 }
840 if self.is_executable_internal() {
842 return Err(InstructionError::ModifiedProgramId);
843 }
844 if !is_zeroed(self.get_data()) {
846 return Err(InstructionError::ModifiedProgramId);
847 }
848 if self.get_owner().to_bytes() == pubkey {
850 return Ok(());
851 }
852 self.touch()?;
853 self.account.copy_into_owner_from_slice(pubkey);
854 Ok(())
855 }
856
857 #[inline]
859 pub fn get_lamports(&self) -> u64 {
860 self.account.lamports()
861 }
862
863 #[cfg(not(target_os = "solana"))]
865 pub fn set_lamports(&mut self, lamports: u64) -> Result<(), InstructionError> {
866 if !self.is_owned_by_current_program() && lamports < self.get_lamports() {
868 return Err(InstructionError::ExternalAccountLamportSpend);
869 }
870 if !self.is_writable() {
872 return Err(InstructionError::ReadonlyLamportChange);
873 }
874 if self.is_executable_internal() {
876 return Err(InstructionError::ExecutableLamportChange);
877 }
878 if self.get_lamports() == lamports {
880 return Ok(());
881 }
882 self.touch()?;
883 self.account.set_lamports(lamports);
884 Ok(())
885 }
886
887 #[cfg(not(target_os = "solana"))]
889 pub fn checked_add_lamports(&mut self, lamports: u64) -> Result<(), InstructionError> {
890 self.set_lamports(
891 self.get_lamports()
892 .checked_add(lamports)
893 .ok_or(InstructionError::ArithmeticOverflow)?,
894 )
895 }
896
897 #[cfg(not(target_os = "solana"))]
899 pub fn checked_sub_lamports(&mut self, lamports: u64) -> Result<(), InstructionError> {
900 self.set_lamports(
901 self.get_lamports()
902 .checked_sub(lamports)
903 .ok_or(InstructionError::ArithmeticOverflow)?,
904 )
905 }
906
907 #[inline]
909 pub fn get_data(&self) -> &[u8] {
910 self.account.data()
911 }
912
913 #[cfg(not(target_os = "solana"))]
915 pub fn get_data_mut(&mut self) -> Result<&mut [u8], InstructionError> {
916 self.can_data_be_changed()?;
917 self.touch()?;
918 self.make_data_mut();
919 Ok(self.account.data_as_mut_slice())
920 }
921
922 #[cfg(not(target_os = "solana"))]
927 pub fn spare_data_capacity_mut(&mut self) -> Result<&mut [MaybeUninit<u8>], InstructionError> {
928 debug_assert!(!self.account.is_shared());
929 Ok(self.account.spare_data_capacity_mut())
930 }
931
932 #[cfg(all(
938 not(target_os = "solana"),
939 any(test, feature = "dev-context-only-utils")
940 ))]
941 pub fn set_data(&mut self, data: Vec<u8>) -> Result<(), InstructionError> {
942 self.can_data_be_resized(data.len())?;
943 self.can_data_be_changed()?;
944 self.touch()?;
945
946 self.update_accounts_resize_delta(data.len())?;
947 self.account.set_data(data);
948 Ok(())
949 }
950
951 #[cfg(not(target_os = "solana"))]
956 pub fn set_data_from_slice(&mut self, data: &[u8]) -> Result<(), InstructionError> {
957 self.can_data_be_resized(data.len())?;
958 self.can_data_be_changed()?;
959 self.touch()?;
960 self.update_accounts_resize_delta(data.len())?;
961 self.account.set_data_from_slice(data);
966
967 Ok(())
968 }
969
970 #[cfg(not(target_os = "solana"))]
974 pub fn set_data_length(&mut self, new_length: usize) -> Result<(), InstructionError> {
975 self.can_data_be_resized(new_length)?;
976 self.can_data_be_changed()?;
977 if self.get_data().len() == new_length {
979 return Ok(());
980 }
981 self.touch()?;
982 self.update_accounts_resize_delta(new_length)?;
983 self.account.resize(new_length, 0);
984 Ok(())
985 }
986
987 #[cfg(not(target_os = "solana"))]
989 pub fn extend_from_slice(&mut self, data: &[u8]) -> Result<(), InstructionError> {
990 let new_len = self.get_data().len().saturating_add(data.len());
991 self.can_data_be_resized(new_len)?;
992 self.can_data_be_changed()?;
993
994 if data.is_empty() {
995 return Ok(());
996 }
997
998 self.touch()?;
999 self.update_accounts_resize_delta(new_len)?;
1000 self.make_data_mut();
1004 self.account.extend_from_slice(data);
1005 Ok(())
1006 }
1007
1008 #[cfg(not(target_os = "solana"))]
1011 pub fn reserve(&mut self, additional: usize) -> Result<(), InstructionError> {
1012 self.make_data_mut();
1017 self.account.reserve(additional);
1018
1019 Ok(())
1020 }
1021
1022 #[cfg(not(target_os = "solana"))]
1024 pub fn capacity(&self) -> usize {
1025 self.account.capacity()
1026 }
1027
1028 #[cfg(not(target_os = "solana"))]
1036 pub fn is_shared(&self) -> bool {
1037 self.account.is_shared()
1038 }
1039
1040 #[cfg(not(target_os = "solana"))]
1041 fn make_data_mut(&mut self) {
1042 if self.account.is_shared() {
1051 self.account.reserve(MAX_PERMITTED_DATA_INCREASE);
1052 }
1053 }
1054
1055 #[cfg(all(not(target_os = "solana"), feature = "bincode"))]
1057 pub fn get_state<T: serde::de::DeserializeOwned>(&self) -> Result<T, InstructionError> {
1058 self.account
1059 .deserialize_data()
1060 .map_err(|_| InstructionError::InvalidAccountData)
1061 }
1062
1063 #[cfg(all(not(target_os = "solana"), feature = "bincode"))]
1065 pub fn set_state<T: serde::Serialize>(&mut self, state: &T) -> Result<(), InstructionError> {
1066 let data = self.get_data_mut()?;
1067 let serialized_size =
1068 bincode::serialized_size(state).map_err(|_| InstructionError::GenericError)?;
1069 if serialized_size > data.len() as u64 {
1070 return Err(InstructionError::AccountDataTooSmall);
1071 }
1072 bincode::serialize_into(&mut *data, state).map_err(|_| InstructionError::GenericError)?;
1073 Ok(())
1074 }
1075
1076 #[cfg(not(target_os = "solana"))]
1079 pub fn is_rent_exempt_at_data_length(&self, data_length: usize) -> bool {
1080 self.transaction_context
1081 .rent
1082 .is_exempt(self.get_lamports(), data_length)
1083 }
1084
1085 #[inline]
1087 #[deprecated(since = "2.1.0", note = "Use `get_owner` instead")]
1088 pub fn is_executable(&self) -> bool {
1089 self.account.executable()
1090 }
1091
1092 #[cfg(not(target_os = "solana"))]
1094 #[inline]
1095 fn is_executable_internal(&self) -> bool {
1096 !self
1097 .transaction_context
1098 .remove_accounts_executable_flag_checks
1099 && self.account.executable()
1100 }
1101
1102 #[cfg(not(target_os = "solana"))]
1104 pub fn set_executable(&mut self, is_executable: bool) -> Result<(), InstructionError> {
1105 if !self
1107 .transaction_context
1108 .rent
1109 .is_exempt(self.get_lamports(), self.get_data().len())
1110 {
1111 return Err(InstructionError::ExecutableAccountNotRentExempt);
1112 }
1113 if !self.is_owned_by_current_program() {
1115 return Err(InstructionError::ExecutableModified);
1116 }
1117 if !self.is_writable() {
1119 return Err(InstructionError::ExecutableModified);
1120 }
1121 if self.is_executable_internal() && !is_executable {
1123 return Err(InstructionError::ExecutableModified);
1124 }
1125 #[allow(deprecated)]
1127 if self.is_executable() == is_executable {
1128 return Ok(());
1129 }
1130 self.touch()?;
1131 self.account.set_executable(is_executable);
1132 Ok(())
1133 }
1134
1135 #[cfg(not(target_os = "solana"))]
1137 #[inline]
1138 pub fn get_rent_epoch(&self) -> u64 {
1139 self.account.rent_epoch()
1140 }
1141
1142 pub fn is_signer(&self) -> bool {
1144 if self.index_in_instruction < self.instruction_context.get_number_of_program_accounts() {
1145 return false;
1146 }
1147 self.instruction_context
1148 .is_instruction_account_signer(
1149 self.index_in_instruction
1150 .saturating_sub(self.instruction_context.get_number_of_program_accounts()),
1151 )
1152 .unwrap_or_default()
1153 }
1154
1155 pub fn is_writable(&self) -> bool {
1157 if self.index_in_instruction < self.instruction_context.get_number_of_program_accounts() {
1158 return false;
1159 }
1160 self.instruction_context
1161 .is_instruction_account_writable(
1162 self.index_in_instruction
1163 .saturating_sub(self.instruction_context.get_number_of_program_accounts()),
1164 )
1165 .unwrap_or_default()
1166 }
1167
1168 pub fn is_owned_by_current_program(&self) -> bool {
1170 self.instruction_context
1171 .get_last_program_key(self.transaction_context)
1172 .map(|key| key == self.get_owner())
1173 .unwrap_or_default()
1174 }
1175
1176 #[cfg(not(target_os = "solana"))]
1178 pub fn can_data_be_changed(&self) -> Result<(), InstructionError> {
1179 if self.is_executable_internal() {
1181 return Err(InstructionError::ExecutableDataModified);
1182 }
1183 if !self.is_writable() {
1185 return Err(InstructionError::ReadonlyDataModified);
1186 }
1187 if !self.is_owned_by_current_program() {
1189 return Err(InstructionError::ExternalAccountDataModified);
1190 }
1191 Ok(())
1192 }
1193
1194 #[cfg(not(target_os = "solana"))]
1196 pub fn can_data_be_resized(&self, new_length: usize) -> Result<(), InstructionError> {
1197 let old_length = self.get_data().len();
1198 if new_length != old_length && !self.is_owned_by_current_program() {
1200 return Err(InstructionError::AccountDataSizeChanged);
1201 }
1202 if new_length > MAX_PERMITTED_DATA_LENGTH as usize {
1204 return Err(InstructionError::InvalidRealloc);
1205 }
1206 let length_delta = (new_length as i64).saturating_sub(old_length as i64);
1208 if self
1209 .transaction_context
1210 .accounts_resize_delta()?
1211 .saturating_add(length_delta)
1212 > MAX_PERMITTED_ACCOUNTS_DATA_ALLOCATIONS_PER_TRANSACTION
1213 {
1214 return Err(InstructionError::MaxAccountsDataAllocationsExceeded);
1215 }
1216 Ok(())
1217 }
1218
1219 #[cfg(not(target_os = "solana"))]
1220 fn touch(&self) -> Result<(), InstructionError> {
1221 self.transaction_context
1222 .accounts()
1223 .touch(self.index_in_transaction)
1224 }
1225
1226 #[cfg(not(target_os = "solana"))]
1227 fn update_accounts_resize_delta(&mut self, new_len: usize) -> Result<(), InstructionError> {
1228 let mut accounts_resize_delta = self
1229 .transaction_context
1230 .accounts_resize_delta
1231 .try_borrow_mut()
1232 .map_err(|_| InstructionError::GenericError)?;
1233 *accounts_resize_delta = accounts_resize_delta
1234 .saturating_add((new_len as i64).saturating_sub(self.get_data().len() as i64));
1235 Ok(())
1236 }
1237}
1238
1239#[cfg(not(target_os = "solana"))]
1241pub struct ExecutionRecord {
1242 pub accounts: Vec<TransactionAccount>,
1243 pub return_data: TransactionReturnData,
1244 pub touched_account_count: u64,
1245 pub accounts_resize_delta: i64,
1246}
1247
1248#[cfg(not(target_os = "solana"))]
1250impl From<TransactionContext> for ExecutionRecord {
1251 fn from(context: TransactionContext) -> Self {
1252 let accounts = Rc::try_unwrap(context.accounts)
1253 .expect("transaction_context.accounts has unexpected outstanding refs");
1254 let touched_account_count = accounts.touched_count() as u64;
1255 let accounts = accounts.into_accounts();
1256 Self {
1257 accounts: Vec::from(Pin::into_inner(context.account_keys))
1258 .into_iter()
1259 .zip(accounts)
1260 .collect(),
1261 return_data: context.return_data,
1262 touched_account_count,
1263 accounts_resize_delta: RefCell::into_inner(context.accounts_resize_delta),
1264 }
1265 }
1266}
1267
1268#[cfg(not(target_os = "solana"))]
1269fn is_zeroed(buf: &[u8]) -> bool {
1270 const ZEROS_LEN: usize = 1024;
1271 const ZEROS: [u8; ZEROS_LEN] = [0; ZEROS_LEN];
1272 let mut chunks = buf.chunks_exact(ZEROS_LEN);
1273
1274 #[allow(clippy::indexing_slicing)]
1275 {
1276 chunks.all(|chunk| chunk == &ZEROS[..])
1277 && chunks.remainder() == &ZEROS[..chunks.remainder().len()]
1278 }
1279}