1#![deny(clippy::indexing_slicing)]
3
4#[cfg(all(not(target_os = "solana"), debug_assertions))]
5use crate::signature::Signature;
6#[cfg(not(target_os = "solana"))]
7use {
8 crate::{
9 account::WritableAccount,
10 rent::Rent,
11 system_instruction::{
12 MAX_PERMITTED_ACCOUNTS_DATA_ALLOCATIONS_PER_TRANSACTION, MAX_PERMITTED_DATA_LENGTH,
13 },
14 },
15 miraland_program::entrypoint::MAX_PERMITTED_DATA_INCREASE,
16 std::mem::MaybeUninit,
17};
18use {
19 crate::{
20 account::{is_builtin, is_executable, AccountSharedData, ReadableAccount},
21 feature_set::FeatureSet,
22 instruction::InstructionError,
23 pubkey::Pubkey,
24 },
25 std::{
26 cell::{Ref, RefCell, RefMut},
27 collections::HashSet,
28 pin::Pin,
29 rc::Rc,
30 },
31};
32
33pub type IndexOfAccount = u16;
35
36#[derive(Clone, Debug, Eq, PartialEq)]
40pub struct InstructionAccount {
41 pub index_in_transaction: IndexOfAccount,
43 pub index_in_caller: IndexOfAccount,
47 pub index_in_callee: IndexOfAccount,
51 pub is_signer: bool,
53 pub is_writable: bool,
55}
56
57pub type TransactionAccount = (Pubkey, AccountSharedData);
59
60#[derive(Clone, Debug, PartialEq)]
61pub struct TransactionAccounts {
62 accounts: Vec<RefCell<AccountSharedData>>,
63 touched_flags: RefCell<Box<[bool]>>,
64}
65
66impl TransactionAccounts {
67 #[cfg(not(target_os = "solana"))]
68 fn new(accounts: Vec<RefCell<AccountSharedData>>) -> TransactionAccounts {
69 TransactionAccounts {
70 touched_flags: RefCell::new(vec![false; accounts.len()].into_boxed_slice()),
71 accounts,
72 }
73 }
74
75 fn len(&self) -> usize {
76 self.accounts.len()
77 }
78
79 pub fn get(&self, index: IndexOfAccount) -> Option<&RefCell<AccountSharedData>> {
80 self.accounts.get(index as usize)
81 }
82
83 #[cfg(not(target_os = "solana"))]
84 pub fn touch(&self, index: IndexOfAccount) -> Result<(), InstructionError> {
85 *self
86 .touched_flags
87 .borrow_mut()
88 .get_mut(index as usize)
89 .ok_or(InstructionError::NotEnoughAccountKeys)? = true;
90 Ok(())
91 }
92
93 #[cfg(not(target_os = "solana"))]
94 pub fn touched_count(&self) -> usize {
95 self.touched_flags
96 .borrow()
97 .iter()
98 .fold(0usize, |accumulator, was_touched| {
99 accumulator.saturating_add(*was_touched as usize)
100 })
101 }
102
103 pub fn try_borrow(
104 &self,
105 index: IndexOfAccount,
106 ) -> Result<Ref<'_, AccountSharedData>, InstructionError> {
107 self.accounts
108 .get(index as usize)
109 .ok_or(InstructionError::MissingAccount)?
110 .try_borrow()
111 .map_err(|_| InstructionError::AccountBorrowFailed)
112 }
113
114 pub fn try_borrow_mut(
115 &self,
116 index: IndexOfAccount,
117 ) -> Result<RefMut<'_, AccountSharedData>, InstructionError> {
118 self.accounts
119 .get(index as usize)
120 .ok_or(InstructionError::MissingAccount)?
121 .try_borrow_mut()
122 .map_err(|_| InstructionError::AccountBorrowFailed)
123 }
124
125 pub fn into_accounts(self) -> Vec<AccountSharedData> {
126 self.accounts
127 .into_iter()
128 .map(|account| account.into_inner())
129 .collect()
130 }
131}
132
133#[derive(Debug, Clone, PartialEq)]
137pub struct TransactionContext {
138 account_keys: Pin<Box<[Pubkey]>>,
139 accounts: Rc<TransactionAccounts>,
140 instruction_stack_capacity: usize,
141 instruction_trace_capacity: usize,
142 instruction_stack: Vec<usize>,
143 instruction_trace: Vec<InstructionContext>,
144 return_data: TransactionReturnData,
145 accounts_resize_delta: RefCell<i64>,
146 #[cfg(not(target_os = "solana"))]
147 rent: Rent,
148 #[cfg(all(not(target_os = "solana"), debug_assertions))]
150 signature: Signature,
151}
152
153impl TransactionContext {
154 #[cfg(not(target_os = "solana"))]
156 pub fn new(
157 transaction_accounts: Vec<TransactionAccount>,
158 rent: Rent,
159 instruction_stack_capacity: usize,
160 instruction_trace_capacity: usize,
161 ) -> Self {
162 let (account_keys, accounts): (Vec<_>, Vec<_>) = transaction_accounts
163 .into_iter()
164 .map(|(key, account)| (key, RefCell::new(account)))
165 .unzip();
166 Self {
167 account_keys: Pin::new(account_keys.into_boxed_slice()),
168 accounts: Rc::new(TransactionAccounts::new(accounts)),
169 instruction_stack_capacity,
170 instruction_trace_capacity,
171 instruction_stack: Vec::with_capacity(instruction_stack_capacity),
172 instruction_trace: vec![InstructionContext::default()],
173 return_data: TransactionReturnData::default(),
174 accounts_resize_delta: RefCell::new(0),
175 rent,
176 #[cfg(all(not(target_os = "solana"), debug_assertions))]
177 signature: Signature::default(),
178 }
179 }
180
181 #[cfg(not(target_os = "solana"))]
183 pub fn deconstruct_without_keys(self) -> Result<Vec<AccountSharedData>, InstructionError> {
184 if !self.instruction_stack.is_empty() {
185 return Err(InstructionError::CallDepth);
186 }
187
188 Ok(Rc::try_unwrap(self.accounts)
189 .expect("transaction_context.accounts has unexpected outstanding refs")
190 .into_accounts())
191 }
192
193 #[cfg(not(target_os = "solana"))]
194 pub fn accounts(&self) -> &Rc<TransactionAccounts> {
195 &self.accounts
196 }
197
198 #[cfg(all(not(target_os = "solana"), debug_assertions))]
200 pub fn set_signature(&mut self, signature: &Signature) {
201 self.signature = *signature;
202 }
203
204 #[cfg(all(not(target_os = "solana"), debug_assertions))]
206 pub fn get_signature(&self) -> &Signature {
207 &self.signature
208 }
209
210 pub fn get_number_of_accounts(&self) -> IndexOfAccount {
212 self.accounts.len() as IndexOfAccount
213 }
214
215 pub fn get_key_of_account_at_index(
217 &self,
218 index_in_transaction: IndexOfAccount,
219 ) -> Result<&Pubkey, InstructionError> {
220 self.account_keys
221 .get(index_in_transaction as usize)
222 .ok_or(InstructionError::NotEnoughAccountKeys)
223 }
224
225 #[cfg(not(target_os = "solana"))]
227 pub fn get_account_at_index(
228 &self,
229 index_in_transaction: IndexOfAccount,
230 ) -> Result<&RefCell<AccountSharedData>, InstructionError> {
231 self.accounts
232 .get(index_in_transaction)
233 .ok_or(InstructionError::NotEnoughAccountKeys)
234 }
235
236 pub fn find_index_of_account(&self, pubkey: &Pubkey) -> Option<IndexOfAccount> {
238 self.account_keys
239 .iter()
240 .position(|key| key == pubkey)
241 .map(|index| index as IndexOfAccount)
242 }
243
244 pub fn find_index_of_program_account(&self, pubkey: &Pubkey) -> Option<IndexOfAccount> {
246 self.account_keys
247 .iter()
248 .rposition(|key| key == pubkey)
249 .map(|index| index as IndexOfAccount)
250 }
251
252 pub fn get_instruction_trace_capacity(&self) -> usize {
254 self.instruction_trace_capacity
255 }
256
257 pub fn get_instruction_trace_length(&self) -> usize {
262 self.instruction_trace.len().saturating_sub(1)
263 }
264
265 pub fn get_instruction_context_at_index_in_trace(
267 &self,
268 index_in_trace: usize,
269 ) -> Result<&InstructionContext, InstructionError> {
270 self.instruction_trace
271 .get(index_in_trace)
272 .ok_or(InstructionError::CallDepth)
273 }
274
275 pub fn get_instruction_context_at_nesting_level(
277 &self,
278 nesting_level: usize,
279 ) -> Result<&InstructionContext, InstructionError> {
280 let index_in_trace = *self
281 .instruction_stack
282 .get(nesting_level)
283 .ok_or(InstructionError::CallDepth)?;
284 let instruction_context = self.get_instruction_context_at_index_in_trace(index_in_trace)?;
285 debug_assert_eq!(instruction_context.nesting_level, nesting_level);
286 Ok(instruction_context)
287 }
288
289 pub fn get_instruction_stack_capacity(&self) -> usize {
291 self.instruction_stack_capacity
292 }
293
294 pub fn get_instruction_context_stack_height(&self) -> usize {
297 self.instruction_stack.len()
298 }
299
300 pub fn get_current_instruction_context(&self) -> Result<&InstructionContext, InstructionError> {
302 let level = self
303 .get_instruction_context_stack_height()
304 .checked_sub(1)
305 .ok_or(InstructionError::CallDepth)?;
306 self.get_instruction_context_at_nesting_level(level)
307 }
308
309 pub fn get_next_instruction_context(
313 &mut self,
314 ) -> Result<&mut InstructionContext, InstructionError> {
315 self.instruction_trace
316 .last_mut()
317 .ok_or(InstructionError::CallDepth)
318 }
319
320 #[cfg(not(target_os = "solana"))]
322 pub fn push(&mut self) -> Result<(), InstructionError> {
323 let nesting_level = self.get_instruction_context_stack_height();
324 let caller_instruction_context = self
325 .instruction_trace
326 .last()
327 .ok_or(InstructionError::CallDepth)?;
328 let callee_instruction_accounts_lamport_sum =
329 self.instruction_accounts_lamport_sum(caller_instruction_context)?;
330 if !self.instruction_stack.is_empty() {
331 let caller_instruction_context = self.get_current_instruction_context()?;
332 let original_caller_instruction_accounts_lamport_sum =
333 caller_instruction_context.instruction_accounts_lamport_sum;
334 let current_caller_instruction_accounts_lamport_sum =
335 self.instruction_accounts_lamport_sum(caller_instruction_context)?;
336 if original_caller_instruction_accounts_lamport_sum
337 != current_caller_instruction_accounts_lamport_sum
338 {
339 return Err(InstructionError::UnbalancedInstruction);
340 }
341 }
342 {
343 let instruction_context = self.get_next_instruction_context()?;
344 instruction_context.nesting_level = nesting_level;
345 instruction_context.instruction_accounts_lamport_sum =
346 callee_instruction_accounts_lamport_sum;
347 }
348 let index_in_trace = self.get_instruction_trace_length();
349 if index_in_trace >= self.instruction_trace_capacity {
350 return Err(InstructionError::MaxInstructionTraceLengthExceeded);
351 }
352 self.instruction_trace.push(InstructionContext::default());
353 if nesting_level >= self.instruction_stack_capacity {
354 return Err(InstructionError::CallDepth);
355 }
356 self.instruction_stack.push(index_in_trace);
357 Ok(())
358 }
359
360 #[cfg(not(target_os = "solana"))]
362 pub fn pop(&mut self) -> Result<(), InstructionError> {
363 if self.instruction_stack.is_empty() {
364 return Err(InstructionError::CallDepth);
365 }
366 let detected_an_unbalanced_instruction =
368 self.get_current_instruction_context()
369 .and_then(|instruction_context| {
370 for account_index in instruction_context.program_accounts.iter() {
372 self.get_account_at_index(*account_index)?
373 .try_borrow_mut()
374 .map_err(|_| InstructionError::AccountBorrowOutstanding)?;
375 }
376 self.instruction_accounts_lamport_sum(instruction_context)
377 .map(|instruction_accounts_lamport_sum| {
378 instruction_context.instruction_accounts_lamport_sum
379 != instruction_accounts_lamport_sum
380 })
381 });
382 self.instruction_stack.pop();
384 if detected_an_unbalanced_instruction? {
385 Err(InstructionError::UnbalancedInstruction)
386 } else {
387 Ok(())
388 }
389 }
390
391 pub fn get_return_data(&self) -> (&Pubkey, &[u8]) {
393 (&self.return_data.program_id, &self.return_data.data)
394 }
395
396 pub fn set_return_data(
398 &mut self,
399 program_id: Pubkey,
400 data: Vec<u8>,
401 ) -> Result<(), InstructionError> {
402 self.return_data = TransactionReturnData { program_id, data };
403 Ok(())
404 }
405
406 #[cfg(not(target_os = "solana"))]
408 fn instruction_accounts_lamport_sum(
409 &self,
410 instruction_context: &InstructionContext,
411 ) -> Result<u128, InstructionError> {
412 let mut instruction_accounts_lamport_sum: u128 = 0;
413 for instruction_account_index in 0..instruction_context.get_number_of_instruction_accounts()
414 {
415 if instruction_context
416 .is_instruction_account_duplicate(instruction_account_index)?
417 .is_some()
418 {
419 continue; }
421 let index_in_transaction = instruction_context
422 .get_index_of_instruction_account_in_transaction(instruction_account_index)?;
423 instruction_accounts_lamport_sum = (self
424 .get_account_at_index(index_in_transaction)?
425 .try_borrow()
426 .map_err(|_| InstructionError::AccountBorrowOutstanding)?
427 .lamports() as u128)
428 .checked_add(instruction_accounts_lamport_sum)
429 .ok_or(InstructionError::ArithmeticOverflow)?;
430 }
431 Ok(instruction_accounts_lamport_sum)
432 }
433
434 pub fn accounts_resize_delta(&self) -> Result<i64, InstructionError> {
436 self.accounts_resize_delta
437 .try_borrow()
438 .map_err(|_| InstructionError::GenericError)
439 .map(|value_ref| *value_ref)
440 }
441}
442
443#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
445pub struct TransactionReturnData {
446 pub program_id: Pubkey,
447 pub data: Vec<u8>,
448}
449
450#[derive(Debug, Clone, Default, Eq, PartialEq)]
454pub struct InstructionContext {
455 nesting_level: usize,
456 instruction_accounts_lamport_sum: u128,
457 program_accounts: Vec<IndexOfAccount>,
458 instruction_accounts: Vec<InstructionAccount>,
459 instruction_data: Vec<u8>,
460}
461
462impl InstructionContext {
463 #[cfg(not(target_os = "solana"))]
465 pub fn configure(
466 &mut self,
467 program_accounts: &[IndexOfAccount],
468 instruction_accounts: &[InstructionAccount],
469 instruction_data: &[u8],
470 ) {
471 self.program_accounts = program_accounts.to_vec();
472 self.instruction_accounts = instruction_accounts.to_vec();
473 self.instruction_data = instruction_data.to_vec();
474 }
475
476 pub fn get_stack_height(&self) -> usize {
480 self.nesting_level.saturating_add(1)
481 }
482
483 pub fn get_number_of_program_accounts(&self) -> IndexOfAccount {
485 self.program_accounts.len() as IndexOfAccount
486 }
487
488 pub fn get_number_of_instruction_accounts(&self) -> IndexOfAccount {
490 self.instruction_accounts.len() as IndexOfAccount
491 }
492
493 pub fn check_number_of_instruction_accounts(
495 &self,
496 expected_at_least: IndexOfAccount,
497 ) -> Result<(), InstructionError> {
498 if self.get_number_of_instruction_accounts() < expected_at_least {
499 Err(InstructionError::NotEnoughAccountKeys)
500 } else {
501 Ok(())
502 }
503 }
504
505 pub fn get_instruction_data(&self) -> &[u8] {
507 &self.instruction_data
508 }
509
510 pub fn find_index_of_program_account(
512 &self,
513 transaction_context: &TransactionContext,
514 pubkey: &Pubkey,
515 ) -> Option<IndexOfAccount> {
516 self.program_accounts
517 .iter()
518 .position(|index_in_transaction| {
519 transaction_context
520 .account_keys
521 .get(*index_in_transaction as usize)
522 == Some(pubkey)
523 })
524 .map(|index| index as IndexOfAccount)
525 }
526
527 pub fn find_index_of_instruction_account(
529 &self,
530 transaction_context: &TransactionContext,
531 pubkey: &Pubkey,
532 ) -> Option<IndexOfAccount> {
533 self.instruction_accounts
534 .iter()
535 .position(|instruction_account| {
536 transaction_context
537 .account_keys
538 .get(instruction_account.index_in_transaction as usize)
539 == Some(pubkey)
540 })
541 .map(|index| index as IndexOfAccount)
542 }
543
544 pub fn get_index_of_program_account_in_transaction(
546 &self,
547 program_account_index: IndexOfAccount,
548 ) -> Result<IndexOfAccount, InstructionError> {
549 Ok(*self
550 .program_accounts
551 .get(program_account_index as usize)
552 .ok_or(InstructionError::NotEnoughAccountKeys)?)
553 }
554
555 pub fn get_index_of_instruction_account_in_transaction(
557 &self,
558 instruction_account_index: IndexOfAccount,
559 ) -> Result<IndexOfAccount, InstructionError> {
560 Ok(self
561 .instruction_accounts
562 .get(instruction_account_index as usize)
563 .ok_or(InstructionError::NotEnoughAccountKeys)?
564 .index_in_transaction as IndexOfAccount)
565 }
566
567 pub fn is_instruction_account_duplicate(
570 &self,
571 instruction_account_index: IndexOfAccount,
572 ) -> Result<Option<IndexOfAccount>, InstructionError> {
573 let index_in_callee = self
574 .instruction_accounts
575 .get(instruction_account_index as usize)
576 .ok_or(InstructionError::NotEnoughAccountKeys)?
577 .index_in_callee;
578 Ok(if index_in_callee == instruction_account_index {
579 None
580 } else {
581 Some(index_in_callee)
582 })
583 }
584
585 pub fn get_last_program_key<'a, 'b: 'a>(
587 &'a self,
588 transaction_context: &'b TransactionContext,
589 ) -> Result<&'b Pubkey, InstructionError> {
590 self.get_index_of_program_account_in_transaction(
591 self.get_number_of_program_accounts().saturating_sub(1),
592 )
593 .and_then(|index_in_transaction| {
594 transaction_context.get_key_of_account_at_index(index_in_transaction)
595 })
596 }
597
598 fn try_borrow_account<'a, 'b: 'a>(
599 &'a self,
600 transaction_context: &'b TransactionContext,
601 index_in_transaction: IndexOfAccount,
602 index_in_instruction: IndexOfAccount,
603 ) -> Result<BorrowedAccount<'a>, InstructionError> {
604 let account = transaction_context
605 .accounts
606 .get(index_in_transaction)
607 .ok_or(InstructionError::MissingAccount)?
608 .try_borrow_mut()
609 .map_err(|_| InstructionError::AccountBorrowFailed)?;
610 Ok(BorrowedAccount {
611 transaction_context,
612 instruction_context: self,
613 index_in_transaction,
614 index_in_instruction,
615 account,
616 })
617 }
618
619 pub fn try_borrow_last_program_account<'a, 'b: 'a>(
621 &'a self,
622 transaction_context: &'b TransactionContext,
623 ) -> Result<BorrowedAccount<'a>, InstructionError> {
624 let result = self.try_borrow_program_account(
625 transaction_context,
626 self.get_number_of_program_accounts().saturating_sub(1),
627 );
628 debug_assert!(result.is_ok());
629 result
630 }
631
632 pub fn try_borrow_program_account<'a, 'b: 'a>(
634 &'a self,
635 transaction_context: &'b TransactionContext,
636 program_account_index: IndexOfAccount,
637 ) -> Result<BorrowedAccount<'a>, InstructionError> {
638 let index_in_transaction =
639 self.get_index_of_program_account_in_transaction(program_account_index)?;
640 self.try_borrow_account(
641 transaction_context,
642 index_in_transaction,
643 program_account_index,
644 )
645 }
646
647 pub fn try_borrow_instruction_account<'a, 'b: 'a>(
649 &'a self,
650 transaction_context: &'b TransactionContext,
651 instruction_account_index: IndexOfAccount,
652 ) -> Result<BorrowedAccount<'a>, InstructionError> {
653 let index_in_transaction =
654 self.get_index_of_instruction_account_in_transaction(instruction_account_index)?;
655 self.try_borrow_account(
656 transaction_context,
657 index_in_transaction,
658 self.get_number_of_program_accounts()
659 .saturating_add(instruction_account_index),
660 )
661 }
662
663 pub fn is_instruction_account_signer(
665 &self,
666 instruction_account_index: IndexOfAccount,
667 ) -> Result<bool, InstructionError> {
668 Ok(self
669 .instruction_accounts
670 .get(instruction_account_index as usize)
671 .ok_or(InstructionError::MissingAccount)?
672 .is_signer)
673 }
674
675 pub fn is_instruction_account_writable(
677 &self,
678 instruction_account_index: IndexOfAccount,
679 ) -> Result<bool, InstructionError> {
680 Ok(self
681 .instruction_accounts
682 .get(instruction_account_index as usize)
683 .ok_or(InstructionError::MissingAccount)?
684 .is_writable)
685 }
686
687 pub fn get_signers(
689 &self,
690 transaction_context: &TransactionContext,
691 ) -> Result<HashSet<Pubkey>, InstructionError> {
692 let mut result = HashSet::new();
693 for instruction_account in self.instruction_accounts.iter() {
694 if instruction_account.is_signer {
695 result.insert(
696 *transaction_context
697 .get_key_of_account_at_index(instruction_account.index_in_transaction)?,
698 );
699 }
700 }
701 Ok(result)
702 }
703}
704
705#[derive(Debug)]
707pub struct BorrowedAccount<'a> {
708 transaction_context: &'a TransactionContext,
709 instruction_context: &'a InstructionContext,
710 index_in_transaction: IndexOfAccount,
711 index_in_instruction: IndexOfAccount,
712 account: RefMut<'a, AccountSharedData>,
713}
714
715impl<'a> BorrowedAccount<'a> {
716 pub fn transaction_context(&self) -> &TransactionContext {
718 self.transaction_context
719 }
720
721 #[inline]
723 pub fn get_index_in_transaction(&self) -> IndexOfAccount {
724 self.index_in_transaction
725 }
726
727 #[inline]
729 pub fn get_key(&self) -> &Pubkey {
730 self.transaction_context
731 .get_key_of_account_at_index(self.index_in_transaction)
732 .unwrap()
733 }
734
735 #[inline]
737 pub fn get_owner(&self) -> &Pubkey {
738 self.account.owner()
739 }
740
741 #[cfg(not(target_os = "solana"))]
743 pub fn set_owner(
744 &mut self,
745 pubkey: &[u8],
746 feature_set: &FeatureSet,
747 ) -> Result<(), InstructionError> {
748 if !self.is_owned_by_current_program() {
750 return Err(InstructionError::ModifiedProgramId);
751 }
752 if !self.is_writable() {
754 return Err(InstructionError::ModifiedProgramId);
755 }
756 if self.is_executable(feature_set) {
758 return Err(InstructionError::ModifiedProgramId);
759 }
760 if !is_zeroed(self.get_data()) {
762 return Err(InstructionError::ModifiedProgramId);
763 }
764 if self.get_owner().to_bytes() == pubkey {
766 return Ok(());
767 }
768 self.touch()?;
769 self.account.copy_into_owner_from_slice(pubkey);
770 Ok(())
771 }
772
773 #[inline]
775 pub fn get_lamports(&self) -> u64 {
776 self.account.lamports()
777 }
778
779 #[cfg(not(target_os = "solana"))]
781 pub fn set_lamports(
782 &mut self,
783 lamports: u64,
784 feature_set: &FeatureSet,
785 ) -> Result<(), InstructionError> {
786 if !self.is_owned_by_current_program() && lamports < self.get_lamports() {
788 return Err(InstructionError::ExternalAccountLamportSpend);
789 }
790 if !self.is_writable() {
792 return Err(InstructionError::ReadonlyLamportChange);
793 }
794 if self.is_executable(feature_set) {
796 return Err(InstructionError::ExecutableLamportChange);
797 }
798 if self.get_lamports() == lamports {
800 return Ok(());
801 }
802 self.touch()?;
803 self.account.set_lamports(lamports);
804 Ok(())
805 }
806
807 #[cfg(not(target_os = "solana"))]
809 pub fn checked_add_lamports(
810 &mut self,
811 lamports: u64,
812 feature_set: &FeatureSet,
813 ) -> Result<(), InstructionError> {
814 self.set_lamports(
815 self.get_lamports()
816 .checked_add(lamports)
817 .ok_or(InstructionError::ArithmeticOverflow)?,
818 feature_set,
819 )
820 }
821
822 #[cfg(not(target_os = "solana"))]
824 pub fn checked_sub_lamports(
825 &mut self,
826 lamports: u64,
827 feature_set: &FeatureSet,
828 ) -> Result<(), InstructionError> {
829 self.set_lamports(
830 self.get_lamports()
831 .checked_sub(lamports)
832 .ok_or(InstructionError::ArithmeticOverflow)?,
833 feature_set,
834 )
835 }
836
837 #[inline]
839 pub fn get_data(&self) -> &[u8] {
840 self.account.data()
841 }
842
843 #[cfg(not(target_os = "solana"))]
845 pub fn get_data_mut(
846 &mut self,
847 feature_set: &FeatureSet,
848 ) -> Result<&mut [u8], InstructionError> {
849 self.can_data_be_changed(feature_set)?;
850 self.touch()?;
851 self.make_data_mut();
852 Ok(self.account.data_as_mut_slice())
853 }
854
855 #[cfg(not(target_os = "solana"))]
860 pub fn spare_data_capacity_mut(&mut self) -> Result<&mut [MaybeUninit<u8>], InstructionError> {
861 debug_assert!(!self.account.is_shared());
862 Ok(self.account.spare_data_capacity_mut())
863 }
864
865 #[cfg(all(
871 not(target_os = "solana"),
872 any(test, feature = "dev-context-only-utils")
873 ))]
874 pub fn set_data(
875 &mut self,
876 data: Vec<u8>,
877 feature_set: &FeatureSet,
878 ) -> Result<(), InstructionError> {
879 self.can_data_be_resized(data.len())?;
880 self.can_data_be_changed(feature_set)?;
881 self.touch()?;
882
883 self.update_accounts_resize_delta(data.len())?;
884 self.account.set_data(data);
885 Ok(())
886 }
887
888 #[cfg(not(target_os = "solana"))]
893 pub fn set_data_from_slice(
894 &mut self,
895 data: &[u8],
896 feature_set: &FeatureSet,
897 ) -> Result<(), InstructionError> {
898 self.can_data_be_resized(data.len())?;
899 self.can_data_be_changed(feature_set)?;
900 self.touch()?;
901 self.update_accounts_resize_delta(data.len())?;
902 self.make_data_mut();
909 self.account.set_data_from_slice(data);
910
911 Ok(())
912 }
913
914 #[cfg(not(target_os = "solana"))]
918 pub fn set_data_length(
919 &mut self,
920 new_length: usize,
921 feature_set: &FeatureSet,
922 ) -> Result<(), InstructionError> {
923 self.can_data_be_resized(new_length)?;
924 self.can_data_be_changed(feature_set)?;
925 if self.get_data().len() == new_length {
927 return Ok(());
928 }
929 self.touch()?;
930 self.update_accounts_resize_delta(new_length)?;
931 self.account.resize(new_length, 0);
932 Ok(())
933 }
934
935 #[cfg(not(target_os = "solana"))]
937 pub fn extend_from_slice(
938 &mut self,
939 data: &[u8],
940 feature_set: &FeatureSet,
941 ) -> Result<(), InstructionError> {
942 let new_len = self.get_data().len().saturating_add(data.len());
943 self.can_data_be_resized(new_len)?;
944 self.can_data_be_changed(feature_set)?;
945
946 if data.is_empty() {
947 return Ok(());
948 }
949
950 self.touch()?;
951 self.update_accounts_resize_delta(new_len)?;
952 self.make_data_mut();
956 self.account.extend_from_slice(data);
957 Ok(())
958 }
959
960 #[cfg(not(target_os = "solana"))]
963 pub fn reserve(&mut self, additional: usize) -> Result<(), InstructionError> {
964 self.make_data_mut();
969 self.account.reserve(additional);
970
971 Ok(())
972 }
973
974 #[cfg(not(target_os = "solana"))]
976 pub fn capacity(&self) -> usize {
977 self.account.capacity()
978 }
979
980 #[cfg(not(target_os = "solana"))]
988 pub fn is_shared(&self) -> bool {
989 self.account.is_shared()
990 }
991
992 #[cfg(not(target_os = "solana"))]
993 fn make_data_mut(&mut self) {
994 if self.account.is_shared() {
1003 self.account.reserve(MAX_PERMITTED_DATA_INCREASE);
1004 }
1005 }
1006
1007 #[cfg(not(target_os = "solana"))]
1009 pub fn get_state<T: serde::de::DeserializeOwned>(&self) -> Result<T, InstructionError> {
1010 self.account
1011 .deserialize_data()
1012 .map_err(|_| InstructionError::InvalidAccountData)
1013 }
1014
1015 #[cfg(not(target_os = "solana"))]
1017 pub fn set_state<T: serde::Serialize>(
1018 &mut self,
1019 state: &T,
1020 feature_set: &FeatureSet,
1021 ) -> Result<(), InstructionError> {
1022 let data = self.get_data_mut(feature_set)?;
1023 let serialized_size =
1024 bincode::serialized_size(state).map_err(|_| InstructionError::GenericError)?;
1025 if serialized_size > data.len() as u64 {
1026 return Err(InstructionError::AccountDataTooSmall);
1027 }
1028 bincode::serialize_into(&mut *data, state).map_err(|_| InstructionError::GenericError)?;
1029 Ok(())
1030 }
1031
1032 #[cfg(not(target_os = "solana"))]
1035 pub fn is_rent_exempt_at_data_length(&self, data_length: usize) -> bool {
1036 self.transaction_context
1037 .rent
1038 .is_exempt(self.get_lamports(), data_length)
1039 }
1040
1041 #[inline]
1043 pub fn is_executable(&self, feature_set: &FeatureSet) -> bool {
1044 is_builtin(&*self.account) || is_executable(&*self.account, feature_set)
1045 }
1046
1047 #[cfg(not(target_os = "solana"))]
1049 pub fn set_executable(&mut self, is_executable: bool) -> Result<(), InstructionError> {
1050 if !self
1052 .transaction_context
1053 .rent
1054 .is_exempt(self.get_lamports(), self.get_data().len())
1055 {
1056 return Err(InstructionError::ExecutableAccountNotRentExempt);
1057 }
1058 if !self.is_owned_by_current_program() {
1060 return Err(InstructionError::ExecutableModified);
1061 }
1062 if !self.is_writable() {
1064 return Err(InstructionError::ExecutableModified);
1065 }
1066 if self.account.executable() && !is_executable {
1068 return Err(InstructionError::ExecutableModified);
1069 }
1070 if self.account.executable() == is_executable {
1072 return Ok(());
1073 }
1074 self.touch()?;
1075 self.account.set_executable(is_executable);
1076 Ok(())
1077 }
1078
1079 #[cfg(not(target_os = "solana"))]
1081 #[inline]
1082 pub fn get_rent_epoch(&self) -> u64 {
1083 self.account.rent_epoch()
1084 }
1085
1086 pub fn is_signer(&self) -> bool {
1088 if self.index_in_instruction < self.instruction_context.get_number_of_program_accounts() {
1089 return false;
1090 }
1091 self.instruction_context
1092 .is_instruction_account_signer(
1093 self.index_in_instruction
1094 .saturating_sub(self.instruction_context.get_number_of_program_accounts()),
1095 )
1096 .unwrap_or_default()
1097 }
1098
1099 pub fn is_writable(&self) -> bool {
1101 if self.index_in_instruction < self.instruction_context.get_number_of_program_accounts() {
1102 return false;
1103 }
1104 self.instruction_context
1105 .is_instruction_account_writable(
1106 self.index_in_instruction
1107 .saturating_sub(self.instruction_context.get_number_of_program_accounts()),
1108 )
1109 .unwrap_or_default()
1110 }
1111
1112 pub fn is_owned_by_current_program(&self) -> bool {
1114 self.instruction_context
1115 .get_last_program_key(self.transaction_context)
1116 .map(|key| key == self.get_owner())
1117 .unwrap_or_default()
1118 }
1119
1120 #[cfg(not(target_os = "solana"))]
1122 pub fn can_data_be_changed(&self, feature_set: &FeatureSet) -> Result<(), InstructionError> {
1123 if self.is_executable(feature_set) {
1125 return Err(InstructionError::ExecutableDataModified);
1126 }
1127 if !self.is_writable() {
1129 return Err(InstructionError::ReadonlyDataModified);
1130 }
1131 if !self.is_owned_by_current_program() {
1133 return Err(InstructionError::ExternalAccountDataModified);
1134 }
1135 Ok(())
1136 }
1137
1138 #[cfg(not(target_os = "solana"))]
1140 pub fn can_data_be_resized(&self, new_length: usize) -> Result<(), InstructionError> {
1141 let old_length = self.get_data().len();
1142 if new_length != old_length && !self.is_owned_by_current_program() {
1144 return Err(InstructionError::AccountDataSizeChanged);
1145 }
1146 if new_length > MAX_PERMITTED_DATA_LENGTH as usize {
1148 return Err(InstructionError::InvalidRealloc);
1149 }
1150 let length_delta = (new_length as i64).saturating_sub(old_length as i64);
1152 if self
1153 .transaction_context
1154 .accounts_resize_delta()?
1155 .saturating_add(length_delta)
1156 > MAX_PERMITTED_ACCOUNTS_DATA_ALLOCATIONS_PER_TRANSACTION
1157 {
1158 return Err(InstructionError::MaxAccountsDataAllocationsExceeded);
1159 }
1160 Ok(())
1161 }
1162
1163 #[cfg(not(target_os = "solana"))]
1164 fn touch(&self) -> Result<(), InstructionError> {
1165 self.transaction_context
1166 .accounts()
1167 .touch(self.index_in_transaction)
1168 }
1169
1170 #[cfg(not(target_os = "solana"))]
1171 fn update_accounts_resize_delta(&mut self, new_len: usize) -> Result<(), InstructionError> {
1172 let mut accounts_resize_delta = self
1173 .transaction_context
1174 .accounts_resize_delta
1175 .try_borrow_mut()
1176 .map_err(|_| InstructionError::GenericError)?;
1177 *accounts_resize_delta = accounts_resize_delta
1178 .saturating_add((new_len as i64).saturating_sub(self.get_data().len() as i64));
1179 Ok(())
1180 }
1181}
1182
1183#[cfg(not(target_os = "solana"))]
1185pub struct ExecutionRecord {
1186 pub accounts: Vec<TransactionAccount>,
1187 pub return_data: TransactionReturnData,
1188 pub touched_account_count: u64,
1189 pub accounts_resize_delta: i64,
1190}
1191
1192#[cfg(not(target_os = "solana"))]
1194impl From<TransactionContext> for ExecutionRecord {
1195 fn from(context: TransactionContext) -> Self {
1196 let accounts = Rc::try_unwrap(context.accounts)
1197 .expect("transaction_context.accounts has unexpected outstanding refs");
1198 let touched_account_count = accounts.touched_count() as u64;
1199 let accounts = accounts.into_accounts();
1200 Self {
1201 accounts: Vec::from(Pin::into_inner(context.account_keys))
1202 .into_iter()
1203 .zip(accounts)
1204 .collect(),
1205 return_data: context.return_data,
1206 touched_account_count,
1207 accounts_resize_delta: RefCell::into_inner(context.accounts_resize_delta),
1208 }
1209 }
1210}
1211
1212#[cfg(not(target_os = "solana"))]
1213fn is_zeroed(buf: &[u8]) -> bool {
1214 const ZEROS_LEN: usize = 1024;
1215 const ZEROS: [u8; ZEROS_LEN] = [0; ZEROS_LEN];
1216 let mut chunks = buf.chunks_exact(ZEROS_LEN);
1217
1218 #[allow(clippy::indexing_slicing)]
1219 {
1220 chunks.all(|chunk| chunk == &ZEROS[..])
1221 && chunks.remainder() == &ZEROS[..chunks.remainder().len()]
1222 }
1223}