1#![deny(clippy::indexing_slicing)]
3
4#[cfg(all(not(target_os = "cbe"), debug_assertions))]
5use crate::signature::Signature;
6#[cfg(not(target_os = "cbe"))]
7use crate::{
8 account::WritableAccount,
9 rent::Rent,
10 system_instruction::{
11 MAX_PERMITTED_ACCOUNTS_DATA_ALLOCATIONS_PER_TRANSACTION, MAX_PERMITTED_DATA_LENGTH,
12 },
13};
14use {
15 crate::{
16 account::{AccountSharedData, ReadableAccount},
17 instruction::InstructionError,
18 pubkey::Pubkey,
19 },
20 std::{
21 cell::{RefCell, RefMut},
22 collections::HashSet,
23 pin::Pin,
24 },
25};
26
27pub type IndexOfAccount = u16;
29
30#[derive(Clone, Debug, Eq, PartialEq)]
34pub struct InstructionAccount {
35 pub index_in_transaction: IndexOfAccount,
37 pub index_in_caller: IndexOfAccount,
41 pub index_in_callee: IndexOfAccount,
45 pub is_signer: bool,
47 pub is_writable: bool,
49}
50
51pub type TransactionAccount = (Pubkey, AccountSharedData);
53
54#[derive(Debug, Clone, PartialEq)]
58pub struct TransactionContext {
59 account_keys: Pin<Box<[Pubkey]>>,
60 accounts: Pin<Box<[RefCell<AccountSharedData>]>>,
61 #[cfg(not(target_os = "cbe"))]
62 account_touched_flags: RefCell<Pin<Box<[bool]>>>,
63 instruction_stack_capacity: usize,
64 instruction_trace_capacity: usize,
65 instruction_stack: Vec<usize>,
66 instruction_trace: Vec<InstructionContext>,
67 return_data: TransactionReturnData,
68 accounts_resize_delta: RefCell<i64>,
69 #[cfg(not(target_os = "cbe"))]
70 rent: Option<Rent>,
71 #[cfg(not(target_os = "cbe"))]
72 is_cap_accounts_data_allocations_per_transaction_enabled: bool,
73 #[cfg(all(not(target_os = "cbe"), debug_assertions))]
75 signature: Signature,
76}
77
78impl TransactionContext {
79 #[cfg(not(target_os = "cbe"))]
81 pub fn new(
82 transaction_accounts: Vec<TransactionAccount>,
83 rent: Option<Rent>,
84 instruction_stack_capacity: usize,
85 instruction_trace_capacity: usize,
86 ) -> Self {
87 let (account_keys, accounts): (Vec<Pubkey>, Vec<RefCell<AccountSharedData>>) =
88 transaction_accounts
89 .into_iter()
90 .map(|(key, account)| (key, RefCell::new(account)))
91 .unzip();
92 let account_touched_flags = vec![false; accounts.len()];
93 Self {
94 account_keys: Pin::new(account_keys.into_boxed_slice()),
95 accounts: Pin::new(accounts.into_boxed_slice()),
96 account_touched_flags: RefCell::new(Pin::new(account_touched_flags.into_boxed_slice())),
97 instruction_stack_capacity,
98 instruction_trace_capacity,
99 instruction_stack: Vec::with_capacity(instruction_stack_capacity),
100 instruction_trace: vec![InstructionContext::default()],
101 return_data: TransactionReturnData::default(),
102 accounts_resize_delta: RefCell::new(0),
103 rent,
104 is_cap_accounts_data_allocations_per_transaction_enabled: false,
105 #[cfg(all(not(target_os = "cbe"), debug_assertions))]
106 signature: Signature::default(),
107 }
108 }
109
110 #[cfg(not(target_os = "cbe"))]
112 pub fn deconstruct_without_keys(self) -> Result<Vec<AccountSharedData>, InstructionError> {
113 if !self.instruction_stack.is_empty() {
114 return Err(InstructionError::CallDepth);
115 }
116 Ok(Vec::from(Pin::into_inner(self.accounts))
117 .into_iter()
118 .map(|account| account.into_inner())
119 .collect())
120 }
121
122 #[cfg(not(target_os = "cbe"))]
124 pub fn is_early_verification_of_account_modifications_enabled(&self) -> bool {
125 self.rent.is_some()
126 }
127
128 #[cfg(all(not(target_os = "cbe"), debug_assertions))]
130 pub fn set_signature(&mut self, signature: &Signature) {
131 self.signature = *signature;
132 }
133
134 #[cfg(all(not(target_os = "cbe"), debug_assertions))]
136 pub fn get_signature(&self) -> &Signature {
137 &self.signature
138 }
139
140 pub fn get_number_of_accounts(&self) -> IndexOfAccount {
142 self.accounts.len() as IndexOfAccount
143 }
144
145 pub fn get_key_of_account_at_index(
147 &self,
148 index_in_transaction: IndexOfAccount,
149 ) -> Result<&Pubkey, InstructionError> {
150 self.account_keys
151 .get(index_in_transaction as usize)
152 .ok_or(InstructionError::NotEnoughAccountKeys)
153 }
154
155 #[cfg(not(target_os = "cbe"))]
157 pub fn get_account_at_index(
158 &self,
159 index_in_transaction: IndexOfAccount,
160 ) -> Result<&RefCell<AccountSharedData>, InstructionError> {
161 self.accounts
162 .get(index_in_transaction as usize)
163 .ok_or(InstructionError::NotEnoughAccountKeys)
164 }
165
166 pub fn find_index_of_account(&self, pubkey: &Pubkey) -> Option<IndexOfAccount> {
168 self.account_keys
169 .iter()
170 .position(|key| key == pubkey)
171 .map(|index| index as IndexOfAccount)
172 }
173
174 pub fn find_index_of_program_account(&self, pubkey: &Pubkey) -> Option<IndexOfAccount> {
176 self.account_keys
177 .iter()
178 .rposition(|key| key == pubkey)
179 .map(|index| index as IndexOfAccount)
180 }
181
182 pub fn get_instruction_trace_capacity(&self) -> usize {
184 self.instruction_trace_capacity
185 }
186
187 pub fn get_instruction_trace_length(&self) -> usize {
192 self.instruction_trace.len().saturating_sub(1)
193 }
194
195 pub fn get_instruction_context_at_index_in_trace(
197 &self,
198 index_in_trace: usize,
199 ) -> Result<&InstructionContext, InstructionError> {
200 self.instruction_trace
201 .get(index_in_trace)
202 .ok_or(InstructionError::CallDepth)
203 }
204
205 pub fn get_instruction_context_at_nesting_level(
207 &self,
208 nesting_level: usize,
209 ) -> Result<&InstructionContext, InstructionError> {
210 let index_in_trace = *self
211 .instruction_stack
212 .get(nesting_level)
213 .ok_or(InstructionError::CallDepth)?;
214 let instruction_context = self.get_instruction_context_at_index_in_trace(index_in_trace)?;
215 debug_assert_eq!(instruction_context.nesting_level, nesting_level);
216 Ok(instruction_context)
217 }
218
219 pub fn get_instruction_stack_capacity(&self) -> usize {
221 self.instruction_stack_capacity
222 }
223
224 pub fn get_instruction_context_stack_height(&self) -> usize {
227 self.instruction_stack.len()
228 }
229
230 pub fn get_current_instruction_context(&self) -> Result<&InstructionContext, InstructionError> {
232 let level = self
233 .get_instruction_context_stack_height()
234 .checked_sub(1)
235 .ok_or(InstructionError::CallDepth)?;
236 self.get_instruction_context_at_nesting_level(level)
237 }
238
239 pub fn get_next_instruction_context(
243 &mut self,
244 ) -> Result<&mut InstructionContext, InstructionError> {
245 self.instruction_trace
246 .last_mut()
247 .ok_or(InstructionError::CallDepth)
248 }
249
250 #[cfg(not(target_os = "cbe"))]
252 pub fn push(&mut self) -> Result<(), InstructionError> {
253 let nesting_level = self.get_instruction_context_stack_height();
254 let caller_instruction_context = self
255 .instruction_trace
256 .last()
257 .ok_or(InstructionError::CallDepth)?;
258 let callee_instruction_accounts_scoobie_sum =
259 self.instruction_accounts_scoobie_sum(caller_instruction_context)?;
260 if !self.instruction_stack.is_empty()
261 && self.is_early_verification_of_account_modifications_enabled()
262 {
263 let caller_instruction_context = self.get_current_instruction_context()?;
264 let original_caller_instruction_accounts_scoobie_sum =
265 caller_instruction_context.instruction_accounts_scoobie_sum;
266 let current_caller_instruction_accounts_scoobie_sum =
267 self.instruction_accounts_scoobie_sum(caller_instruction_context)?;
268 if original_caller_instruction_accounts_scoobie_sum
269 != current_caller_instruction_accounts_scoobie_sum
270 {
271 return Err(InstructionError::UnbalancedInstruction);
272 }
273 }
274 {
275 let mut instruction_context = self.get_next_instruction_context()?;
276 instruction_context.nesting_level = nesting_level;
277 instruction_context.instruction_accounts_scoobie_sum =
278 callee_instruction_accounts_scoobie_sum;
279 }
280 let index_in_trace = self.get_instruction_trace_length();
281 if index_in_trace >= self.instruction_trace_capacity {
282 return Err(InstructionError::MaxInstructionTraceLengthExceeded);
283 }
284 self.instruction_trace.push(InstructionContext::default());
285 if nesting_level >= self.instruction_stack_capacity {
286 return Err(InstructionError::CallDepth);
287 }
288 self.instruction_stack.push(index_in_trace);
289 Ok(())
290 }
291
292 #[cfg(not(target_os = "cbe"))]
294 pub fn pop(&mut self) -> Result<(), InstructionError> {
295 if self.instruction_stack.is_empty() {
296 return Err(InstructionError::CallDepth);
297 }
298 let detected_an_unbalanced_instruction =
300 if self.is_early_verification_of_account_modifications_enabled() {
301 self.get_current_instruction_context()
302 .and_then(|instruction_context| {
303 for account_index in instruction_context.program_accounts.iter() {
305 self.get_account_at_index(*account_index)?
306 .try_borrow_mut()
307 .map_err(|_| InstructionError::AccountBorrowOutstanding)?;
308 }
309 self.instruction_accounts_scoobie_sum(instruction_context)
310 .map(|instruction_accounts_scoobie_sum| {
311 instruction_context.instruction_accounts_scoobie_sum
312 != instruction_accounts_scoobie_sum
313 })
314 })
315 } else {
316 Ok(false)
317 };
318 self.instruction_stack.pop();
320 if detected_an_unbalanced_instruction? {
321 Err(InstructionError::UnbalancedInstruction)
322 } else {
323 Ok(())
324 }
325 }
326
327 pub fn get_return_data(&self) -> (&Pubkey, &[u8]) {
329 (&self.return_data.program_id, &self.return_data.data)
330 }
331
332 pub fn set_return_data(
334 &mut self,
335 program_id: Pubkey,
336 data: Vec<u8>,
337 ) -> Result<(), InstructionError> {
338 self.return_data = TransactionReturnData { program_id, data };
339 Ok(())
340 }
341
342 #[cfg(not(target_os = "cbe"))]
344 fn instruction_accounts_scoobie_sum(
345 &self,
346 instruction_context: &InstructionContext,
347 ) -> Result<u128, InstructionError> {
348 if !self.is_early_verification_of_account_modifications_enabled() {
349 return Ok(0);
350 }
351 let mut instruction_accounts_scoobie_sum: u128 = 0;
352 for instruction_account_index in 0..instruction_context.get_number_of_instruction_accounts()
353 {
354 if instruction_context
355 .is_instruction_account_duplicate(instruction_account_index)?
356 .is_some()
357 {
358 continue; }
360 let index_in_transaction = instruction_context
361 .get_index_of_instruction_account_in_transaction(instruction_account_index)?;
362 instruction_accounts_scoobie_sum = (self
363 .get_account_at_index(index_in_transaction)?
364 .try_borrow()
365 .map_err(|_| InstructionError::AccountBorrowOutstanding)?
366 .scoobies() as u128)
367 .checked_add(instruction_accounts_scoobie_sum)
368 .ok_or(InstructionError::ArithmeticOverflow)?;
369 }
370 Ok(instruction_accounts_scoobie_sum)
371 }
372
373 pub fn accounts_resize_delta(&self) -> Result<i64, InstructionError> {
375 self.accounts_resize_delta
376 .try_borrow()
377 .map_err(|_| InstructionError::GenericError)
378 .map(|value_ref| *value_ref)
379 }
380
381 #[cfg(not(target_os = "cbe"))]
383 pub fn enable_cap_accounts_data_allocations_per_transaction(&mut self) {
384 self.is_cap_accounts_data_allocations_per_transaction_enabled = true;
385 }
386}
387
388#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
390pub struct TransactionReturnData {
391 pub program_id: Pubkey,
392 pub data: Vec<u8>,
393}
394
395#[derive(Debug, Clone, Default, Eq, PartialEq)]
399pub struct InstructionContext {
400 nesting_level: usize,
401 instruction_accounts_scoobie_sum: u128,
402 program_accounts: Vec<IndexOfAccount>,
403 instruction_accounts: Vec<InstructionAccount>,
404 instruction_data: Vec<u8>,
405}
406
407impl InstructionContext {
408 #[cfg(not(target_os = "cbe"))]
410 pub fn configure(
411 &mut self,
412 program_accounts: &[IndexOfAccount],
413 instruction_accounts: &[InstructionAccount],
414 instruction_data: &[u8],
415 ) {
416 self.program_accounts = program_accounts.to_vec();
417 self.instruction_accounts = instruction_accounts.to_vec();
418 self.instruction_data = instruction_data.to_vec();
419 }
420
421 pub fn get_stack_height(&self) -> usize {
425 self.nesting_level.saturating_add(1)
426 }
427
428 pub fn get_instruction_accounts_scoobie_sum(&self) -> u128 {
430 self.instruction_accounts_scoobie_sum
431 }
432
433 pub fn get_number_of_program_accounts(&self) -> IndexOfAccount {
435 self.program_accounts.len() as IndexOfAccount
436 }
437
438 pub fn get_number_of_instruction_accounts(&self) -> IndexOfAccount {
440 self.instruction_accounts.len() as IndexOfAccount
441 }
442
443 pub fn check_number_of_instruction_accounts(
445 &self,
446 expected_at_least: IndexOfAccount,
447 ) -> Result<(), InstructionError> {
448 if self.get_number_of_instruction_accounts() < expected_at_least {
449 Err(InstructionError::NotEnoughAccountKeys)
450 } else {
451 Ok(())
452 }
453 }
454
455 pub fn get_instruction_data(&self) -> &[u8] {
457 &self.instruction_data
458 }
459
460 pub fn find_index_of_program_account(
462 &self,
463 transaction_context: &TransactionContext,
464 pubkey: &Pubkey,
465 ) -> Option<IndexOfAccount> {
466 self.program_accounts
467 .iter()
468 .position(|index_in_transaction| {
469 transaction_context
470 .account_keys
471 .get(*index_in_transaction as usize)
472 == Some(pubkey)
473 })
474 .map(|index| index as IndexOfAccount)
475 }
476
477 pub fn find_index_of_instruction_account(
479 &self,
480 transaction_context: &TransactionContext,
481 pubkey: &Pubkey,
482 ) -> Option<IndexOfAccount> {
483 self.instruction_accounts
484 .iter()
485 .position(|instruction_account| {
486 transaction_context
487 .account_keys
488 .get(instruction_account.index_in_transaction as usize)
489 == Some(pubkey)
490 })
491 .map(|index| index as IndexOfAccount)
492 }
493
494 pub fn get_index_of_program_account_in_transaction(
496 &self,
497 program_account_index: IndexOfAccount,
498 ) -> Result<IndexOfAccount, InstructionError> {
499 Ok(*self
500 .program_accounts
501 .get(program_account_index as usize)
502 .ok_or(InstructionError::NotEnoughAccountKeys)?)
503 }
504
505 pub fn get_index_of_instruction_account_in_transaction(
507 &self,
508 instruction_account_index: IndexOfAccount,
509 ) -> Result<IndexOfAccount, InstructionError> {
510 Ok(self
511 .instruction_accounts
512 .get(instruction_account_index as usize)
513 .ok_or(InstructionError::NotEnoughAccountKeys)?
514 .index_in_transaction as IndexOfAccount)
515 }
516
517 pub fn is_instruction_account_duplicate(
520 &self,
521 instruction_account_index: IndexOfAccount,
522 ) -> Result<Option<IndexOfAccount>, InstructionError> {
523 let index_in_callee = self
524 .instruction_accounts
525 .get(instruction_account_index as usize)
526 .ok_or(InstructionError::NotEnoughAccountKeys)?
527 .index_in_callee;
528 Ok(if index_in_callee == instruction_account_index {
529 None
530 } else {
531 Some(index_in_callee)
532 })
533 }
534
535 pub fn get_last_program_key<'a, 'b: 'a>(
537 &'a self,
538 transaction_context: &'b TransactionContext,
539 ) -> Result<&'b Pubkey, InstructionError> {
540 self.get_index_of_program_account_in_transaction(
541 self.get_number_of_program_accounts().saturating_sub(1),
542 )
543 .and_then(|index_in_transaction| {
544 transaction_context.get_key_of_account_at_index(index_in_transaction)
545 })
546 }
547
548 fn try_borrow_account<'a, 'b: 'a>(
549 &'a self,
550 transaction_context: &'b TransactionContext,
551 index_in_transaction: IndexOfAccount,
552 index_in_instruction: IndexOfAccount,
553 ) -> Result<BorrowedAccount<'a>, InstructionError> {
554 let account = transaction_context
555 .accounts
556 .get(index_in_transaction as usize)
557 .ok_or(InstructionError::MissingAccount)?
558 .try_borrow_mut()
559 .map_err(|_| InstructionError::AccountBorrowFailed)?;
560 Ok(BorrowedAccount {
561 transaction_context,
562 instruction_context: self,
563 index_in_transaction,
564 index_in_instruction,
565 account,
566 })
567 }
568
569 pub fn try_borrow_last_program_account<'a, 'b: 'a>(
571 &'a self,
572 transaction_context: &'b TransactionContext,
573 ) -> Result<BorrowedAccount<'a>, InstructionError> {
574 let result = self.try_borrow_program_account(
575 transaction_context,
576 self.get_number_of_program_accounts().saturating_sub(1),
577 );
578 debug_assert!(result.is_ok());
579 result
580 }
581
582 pub fn try_borrow_program_account<'a, 'b: 'a>(
584 &'a self,
585 transaction_context: &'b TransactionContext,
586 program_account_index: IndexOfAccount,
587 ) -> Result<BorrowedAccount<'a>, InstructionError> {
588 let index_in_transaction =
589 self.get_index_of_program_account_in_transaction(program_account_index)?;
590 self.try_borrow_account(
591 transaction_context,
592 index_in_transaction,
593 program_account_index,
594 )
595 }
596
597 pub fn try_borrow_instruction_account<'a, 'b: 'a>(
599 &'a self,
600 transaction_context: &'b TransactionContext,
601 instruction_account_index: IndexOfAccount,
602 ) -> Result<BorrowedAccount<'a>, InstructionError> {
603 let index_in_transaction =
604 self.get_index_of_instruction_account_in_transaction(instruction_account_index)?;
605 self.try_borrow_account(
606 transaction_context,
607 index_in_transaction,
608 self.get_number_of_program_accounts()
609 .saturating_add(instruction_account_index),
610 )
611 }
612
613 pub fn is_instruction_account_signer(
615 &self,
616 instruction_account_index: IndexOfAccount,
617 ) -> Result<bool, InstructionError> {
618 Ok(self
619 .instruction_accounts
620 .get(instruction_account_index as usize)
621 .ok_or(InstructionError::MissingAccount)?
622 .is_signer)
623 }
624
625 pub fn is_instruction_account_writable(
627 &self,
628 instruction_account_index: IndexOfAccount,
629 ) -> Result<bool, InstructionError> {
630 Ok(self
631 .instruction_accounts
632 .get(instruction_account_index as usize)
633 .ok_or(InstructionError::MissingAccount)?
634 .is_writable)
635 }
636
637 pub fn get_signers(
639 &self,
640 transaction_context: &TransactionContext,
641 ) -> Result<HashSet<Pubkey>, InstructionError> {
642 let mut result = HashSet::new();
643 for instruction_account in self.instruction_accounts.iter() {
644 if instruction_account.is_signer {
645 result.insert(
646 *transaction_context
647 .get_key_of_account_at_index(instruction_account.index_in_transaction)?,
648 );
649 }
650 }
651 Ok(result)
652 }
653}
654
655#[derive(Debug)]
657pub struct BorrowedAccount<'a> {
658 transaction_context: &'a TransactionContext,
659 instruction_context: &'a InstructionContext,
660 index_in_transaction: IndexOfAccount,
661 index_in_instruction: IndexOfAccount,
662 account: RefMut<'a, AccountSharedData>,
663}
664
665impl<'a> BorrowedAccount<'a> {
666 #[inline]
668 pub fn get_index_in_transaction(&self) -> IndexOfAccount {
669 self.index_in_transaction
670 }
671
672 #[inline]
674 pub fn get_key(&self) -> &Pubkey {
675 self.transaction_context
676 .get_key_of_account_at_index(self.index_in_transaction)
677 .unwrap()
678 }
679
680 #[inline]
682 pub fn get_owner(&self) -> &Pubkey {
683 self.account.owner()
684 }
685
686 #[cfg(not(target_os = "cbe"))]
688 pub fn set_owner(&mut self, pubkey: &[u8]) -> Result<(), InstructionError> {
689 if self
690 .transaction_context
691 .is_early_verification_of_account_modifications_enabled()
692 {
693 if !self.is_owned_by_current_program() {
695 return Err(InstructionError::ModifiedProgramId);
696 }
697 if !self.is_writable() {
699 return Err(InstructionError::ModifiedProgramId);
700 }
701 if self.is_executable() {
703 return Err(InstructionError::ModifiedProgramId);
704 }
705 if !is_zeroed(self.get_data()) {
707 return Err(InstructionError::ModifiedProgramId);
708 }
709 if self.get_owner().to_bytes() == pubkey {
711 return Ok(());
712 }
713 self.touch()?;
714 }
715 self.account.copy_into_owner_from_slice(pubkey);
716 Ok(())
717 }
718
719 #[inline]
721 pub fn get_scoobies(&self) -> u64 {
722 self.account.scoobies()
723 }
724
725 #[cfg(not(target_os = "cbe"))]
727 pub fn set_scoobies(&mut self, scoobies: u64) -> Result<(), InstructionError> {
728 if self
729 .transaction_context
730 .is_early_verification_of_account_modifications_enabled()
731 {
732 if !self.is_owned_by_current_program() && scoobies < self.get_scoobies() {
734 return Err(InstructionError::ExternalAccountScoobieSpend);
735 }
736 if !self.is_writable() {
738 return Err(InstructionError::ReadonlyScoobieChange);
739 }
740 if self.is_executable() {
742 return Err(InstructionError::ExecutableScoobieChange);
743 }
744 if self.get_scoobies() == scoobies {
746 return Ok(());
747 }
748 self.touch()?;
749 }
750 self.account.set_scoobies(scoobies);
751 Ok(())
752 }
753
754 #[cfg(not(target_os = "cbe"))]
756 pub fn checked_add_scoobies(&mut self, scoobies: u64) -> Result<(), InstructionError> {
757 self.set_scoobies(
758 self.get_scoobies()
759 .checked_add(scoobies)
760 .ok_or(InstructionError::ArithmeticOverflow)?,
761 )
762 }
763
764 #[cfg(not(target_os = "cbe"))]
766 pub fn checked_sub_scoobies(&mut self, scoobies: u64) -> Result<(), InstructionError> {
767 self.set_scoobies(
768 self.get_scoobies()
769 .checked_sub(scoobies)
770 .ok_or(InstructionError::ArithmeticOverflow)?,
771 )
772 }
773
774 #[inline]
776 pub fn get_data(&self) -> &[u8] {
777 self.account.data()
778 }
779
780 #[cfg(not(target_os = "cbe"))]
782 pub fn get_data_mut(&mut self) -> Result<&mut [u8], InstructionError> {
783 self.can_data_be_changed()?;
784 self.touch()?;
785 Ok(self.account.data_as_mut_slice())
786 }
787
788 #[cfg(not(target_os = "cbe"))]
795 pub fn set_data(&mut self, data: Vec<u8>) -> Result<(), InstructionError> {
796 self.can_data_be_resized(data.len())?;
797 self.can_data_be_changed()?;
798 self.touch()?;
799 self.update_accounts_resize_delta(data.len())?;
800 self.account.set_data(data);
801
802 Ok(())
803 }
804
805 #[cfg(not(target_os = "cbe"))]
812 pub fn set_data_from_slice(&mut self, data: &[u8]) -> Result<(), InstructionError> {
813 self.can_data_be_resized(data.len())?;
814 self.can_data_be_changed()?;
815 self.touch()?;
816 self.update_accounts_resize_delta(data.len())?;
817 self.account.set_data_from_slice(data);
818
819 Ok(())
820 }
821
822 #[cfg(not(target_os = "cbe"))]
826 pub fn set_data_length(&mut self, new_length: usize) -> Result<(), InstructionError> {
827 self.can_data_be_resized(new_length)?;
828 self.can_data_be_changed()?;
829 if self.get_data().len() == new_length {
831 return Ok(());
832 }
833 self.touch()?;
834 self.update_accounts_resize_delta(new_length)?;
835 self.account.data_mut().resize(new_length, 0);
836 Ok(())
837 }
838
839 #[cfg(not(target_os = "cbe"))]
841 pub fn extend_from_slice(&mut self, data: &[u8]) -> Result<(), InstructionError> {
842 let new_len = self.get_data().len().saturating_add(data.len());
843 self.can_data_be_resized(new_len)?;
844 self.can_data_be_changed()?;
845
846 if data.is_empty() {
847 return Ok(());
848 }
849
850 self.touch()?;
851 self.update_accounts_resize_delta(new_len)?;
852 self.account.data_mut().extend_from_slice(data);
853 Ok(())
854 }
855
856 #[cfg(not(target_os = "cbe"))]
858 pub fn get_state<T: serde::de::DeserializeOwned>(&self) -> Result<T, InstructionError> {
859 self.account
860 .deserialize_data()
861 .map_err(|_| InstructionError::InvalidAccountData)
862 }
863
864 #[cfg(not(target_os = "cbe"))]
866 pub fn set_state<T: serde::Serialize>(&mut self, state: &T) -> Result<(), InstructionError> {
867 let data = self.get_data_mut()?;
868 let serialized_size =
869 bincode::serialized_size(state).map_err(|_| InstructionError::GenericError)?;
870 if serialized_size > data.len() as u64 {
871 return Err(InstructionError::AccountDataTooSmall);
872 }
873 bincode::serialize_into(&mut *data, state).map_err(|_| InstructionError::GenericError)?;
874 Ok(())
875 }
876
877 #[inline]
879 pub fn is_executable(&self) -> bool {
880 self.account.executable()
881 }
882
883 #[cfg(not(target_os = "cbe"))]
885 pub fn set_executable(&mut self, is_executable: bool) -> Result<(), InstructionError> {
886 if let Some(rent) = self.transaction_context.rent {
887 if !rent.is_exempt(self.get_scoobies(), self.get_data().len()) {
889 return Err(InstructionError::ExecutableAccountNotRentExempt);
890 }
891 if !self.is_owned_by_current_program() {
893 return Err(InstructionError::ExecutableModified);
894 }
895 if !self.is_writable() {
897 return Err(InstructionError::ExecutableModified);
898 }
899 if self.is_executable() && !is_executable {
901 return Err(InstructionError::ExecutableModified);
902 }
903 if self.is_executable() == is_executable {
905 return Ok(());
906 }
907 self.touch()?;
908 }
909 self.account.set_executable(is_executable);
910 Ok(())
911 }
912
913 #[cfg(not(target_os = "cbe"))]
915 #[inline]
916 pub fn get_rent_epoch(&self) -> u64 {
917 self.account.rent_epoch()
918 }
919
920 pub fn is_signer(&self) -> bool {
922 if self.index_in_instruction < self.instruction_context.get_number_of_program_accounts() {
923 return false;
924 }
925 self.instruction_context
926 .is_instruction_account_signer(
927 self.index_in_instruction
928 .saturating_sub(self.instruction_context.get_number_of_program_accounts()),
929 )
930 .unwrap_or_default()
931 }
932
933 pub fn is_writable(&self) -> bool {
935 if self.index_in_instruction < self.instruction_context.get_number_of_program_accounts() {
936 return false;
937 }
938 self.instruction_context
939 .is_instruction_account_writable(
940 self.index_in_instruction
941 .saturating_sub(self.instruction_context.get_number_of_program_accounts()),
942 )
943 .unwrap_or_default()
944 }
945
946 pub fn is_owned_by_current_program(&self) -> bool {
948 self.instruction_context
949 .get_last_program_key(self.transaction_context)
950 .map(|key| key == self.get_owner())
951 .unwrap_or_default()
952 }
953
954 #[cfg(not(target_os = "cbe"))]
956 pub fn can_data_be_changed(&self) -> Result<(), InstructionError> {
957 if !self
958 .transaction_context
959 .is_early_verification_of_account_modifications_enabled()
960 {
961 return Ok(());
962 }
963 if self.is_executable() {
965 return Err(InstructionError::ExecutableDataModified);
966 }
967 if !self.is_writable() {
969 return Err(InstructionError::ReadonlyDataModified);
970 }
971 if !self.is_owned_by_current_program() {
973 return Err(InstructionError::ExternalAccountDataModified);
974 }
975 Ok(())
976 }
977
978 #[cfg(not(target_os = "cbe"))]
980 pub fn can_data_be_resized(&self, new_length: usize) -> Result<(), InstructionError> {
981 if !self
982 .transaction_context
983 .is_early_verification_of_account_modifications_enabled()
984 {
985 return Ok(());
986 }
987 let old_length = self.get_data().len();
988 if new_length != old_length && !self.is_owned_by_current_program() {
990 return Err(InstructionError::AccountDataSizeChanged);
991 }
992 if new_length > MAX_PERMITTED_DATA_LENGTH as usize {
994 return Err(InstructionError::InvalidRealloc);
995 }
996 if self
997 .transaction_context
998 .is_cap_accounts_data_allocations_per_transaction_enabled
999 {
1000 let length_delta = (new_length as i64).saturating_sub(old_length as i64);
1002 if self
1003 .transaction_context
1004 .accounts_resize_delta()?
1005 .saturating_add(length_delta)
1006 > MAX_PERMITTED_ACCOUNTS_DATA_ALLOCATIONS_PER_TRANSACTION
1007 {
1008 return Err(InstructionError::MaxAccountsDataAllocationsExceeded);
1009 }
1010 }
1011 Ok(())
1012 }
1013
1014 #[cfg(not(target_os = "cbe"))]
1015 fn touch(&self) -> Result<(), InstructionError> {
1016 if self
1017 .transaction_context
1018 .is_early_verification_of_account_modifications_enabled()
1019 {
1020 *self
1021 .transaction_context
1022 .account_touched_flags
1023 .try_borrow_mut()
1024 .map_err(|_| InstructionError::GenericError)?
1025 .get_mut(self.index_in_transaction as usize)
1026 .ok_or(InstructionError::NotEnoughAccountKeys)? = true;
1027 }
1028 Ok(())
1029 }
1030
1031 #[cfg(not(target_os = "cbe"))]
1032 fn update_accounts_resize_delta(&mut self, new_len: usize) -> Result<(), InstructionError> {
1033 let mut accounts_resize_delta = self
1034 .transaction_context
1035 .accounts_resize_delta
1036 .try_borrow_mut()
1037 .map_err(|_| InstructionError::GenericError)?;
1038 *accounts_resize_delta = accounts_resize_delta
1039 .saturating_add((new_len as i64).saturating_sub(self.get_data().len() as i64));
1040 Ok(())
1041 }
1042}
1043
1044#[cfg(not(target_os = "cbe"))]
1046pub struct ExecutionRecord {
1047 pub accounts: Vec<TransactionAccount>,
1048 pub return_data: TransactionReturnData,
1049 pub touched_account_count: u64,
1050 pub accounts_resize_delta: i64,
1051}
1052
1053#[cfg(not(target_os = "cbe"))]
1055impl From<TransactionContext> for ExecutionRecord {
1056 fn from(context: TransactionContext) -> Self {
1057 let account_touched_flags = context
1058 .account_touched_flags
1059 .try_borrow()
1060 .expect("borrowing transaction_context.account_touched_flags failed");
1061 let touched_account_count = account_touched_flags
1062 .iter()
1063 .fold(0u64, |accumulator, was_touched| {
1064 accumulator.saturating_add(*was_touched as u64)
1065 });
1066 Self {
1067 accounts: Vec::from(Pin::into_inner(context.account_keys))
1068 .into_iter()
1069 .zip(
1070 Vec::from(Pin::into_inner(context.accounts))
1071 .into_iter()
1072 .map(|account| account.into_inner()),
1073 )
1074 .collect(),
1075 return_data: context.return_data,
1076 touched_account_count,
1077 accounts_resize_delta: RefCell::into_inner(context.accounts_resize_delta),
1078 }
1079 }
1080}
1081
1082#[cfg(not(target_os = "cbe"))]
1083fn is_zeroed(buf: &[u8]) -> bool {
1084 const ZEROS_LEN: usize = 1024;
1085 const ZEROS: [u8; ZEROS_LEN] = [0; ZEROS_LEN];
1086 let mut chunks = buf.chunks_exact(ZEROS_LEN);
1087
1088 #[allow(clippy::indexing_slicing)]
1089 {
1090 chunks.all(|chunk| chunk == &ZEROS[..])
1091 && chunks.remainder() == &ZEROS[..chunks.remainder().len()]
1092 }
1093}