1use {
4 crate::{
5 account::{AccountSharedData, ReadableAccount, WritableAccount},
6 instruction::InstructionError,
7 pubkey::Pubkey,
8 rent::Rent,
9 system_instruction::MAX_PERMITTED_DATA_LENGTH,
10 },
11 std::{
12 cell::{RefCell, RefMut},
13 collections::HashSet,
14 pin::Pin,
15 },
16};
17
18pub type TransactionAccount = (Pubkey, AccountSharedData);
19
20#[derive(Clone, Debug)]
24pub struct InstructionAccount {
25 pub index_in_transaction: usize,
27 pub index_in_caller: usize,
31 pub index_in_callee: usize,
35 pub is_signer: bool,
37 pub is_writable: bool,
39}
40
41#[derive(Debug)]
45pub struct TransactionContext {
46 account_keys: Pin<Box<[Pubkey]>>,
47 accounts: Pin<Box<[RefCell<AccountSharedData>]>>,
48 account_touched_flags: RefCell<Pin<Box<[bool]>>>,
49 instruction_context_capacity: usize,
50 instruction_stack: Vec<usize>,
51 number_of_instructions_at_transaction_level: usize,
52 instruction_trace: InstructionTrace,
53 return_data: TransactionReturnData,
54 accounts_resize_delta: RefCell<i64>,
55 rent: Option<Rent>,
56}
57
58impl TransactionContext {
59 pub fn new(
61 transaction_accounts: Vec<TransactionAccount>,
62 rent: Option<Rent>,
63 instruction_context_capacity: usize,
64 number_of_instructions_at_transaction_level: usize,
65 ) -> Self {
66 let (account_keys, accounts): (Vec<Pubkey>, Vec<RefCell<AccountSharedData>>) =
67 transaction_accounts
68 .into_iter()
69 .map(|(key, account)| (key, RefCell::new(account)))
70 .unzip();
71 let account_touched_flags = vec![false; accounts.len()];
72 Self {
73 account_keys: Pin::new(account_keys.into_boxed_slice()),
74 accounts: Pin::new(accounts.into_boxed_slice()),
75 account_touched_flags: RefCell::new(Pin::new(account_touched_flags.into_boxed_slice())),
76 instruction_context_capacity,
77 instruction_stack: Vec::with_capacity(instruction_context_capacity),
78 number_of_instructions_at_transaction_level,
79 instruction_trace: Vec::with_capacity(number_of_instructions_at_transaction_level),
80 return_data: TransactionReturnData::default(),
81 accounts_resize_delta: RefCell::new(0),
82 rent,
83 }
84 }
85
86 pub fn deconstruct_without_keys(self) -> Result<Vec<AccountSharedData>, InstructionError> {
88 if !self.instruction_stack.is_empty() {
89 return Err(InstructionError::CallDepth);
90 }
91 Ok(Vec::from(Pin::into_inner(self.accounts))
92 .into_iter()
93 .map(|account| account.into_inner())
94 .collect())
95 }
96
97 pub fn is_early_verification_of_account_modifications_enabled(&self) -> bool {
99 self.rent.is_some()
100 }
101
102 pub fn get_number_of_accounts(&self) -> usize {
104 self.accounts.len()
105 }
106
107 pub fn get_key_of_account_at_index(
109 &self,
110 index_in_transaction: usize,
111 ) -> Result<&Pubkey, InstructionError> {
112 self.account_keys
113 .get(index_in_transaction)
114 .ok_or(InstructionError::NotEnoughAccountKeys)
115 }
116
117 pub fn get_keys_of_accounts(&self) -> &[Pubkey] {
119 &self.account_keys
120 }
121
122 pub fn get_account_at_index(
124 &self,
125 index_in_transaction: usize,
126 ) -> Result<&RefCell<AccountSharedData>, InstructionError> {
127 self.accounts
128 .get(index_in_transaction)
129 .ok_or(InstructionError::NotEnoughAccountKeys)
130 }
131
132 pub fn find_index_of_account(&self, pubkey: &Pubkey) -> Option<usize> {
134 self.account_keys.iter().position(|key| key == pubkey)
135 }
136
137 pub fn find_index_of_program_account(&self, pubkey: &Pubkey) -> Option<usize> {
139 self.account_keys.iter().rposition(|key| key == pubkey)
140 }
141
142 pub fn get_instruction_context_at(
144 &self,
145 level: usize,
146 ) -> Result<&InstructionContext, InstructionError> {
147 let top_level_index = *self
148 .instruction_stack
149 .first()
150 .ok_or(InstructionError::CallDepth)?;
151 let cpi_index = if level == 0 {
152 0
153 } else {
154 *self
155 .instruction_stack
156 .get(level)
157 .ok_or(InstructionError::CallDepth)?
158 };
159 let instruction_context = self
160 .instruction_trace
161 .get(top_level_index)
162 .and_then(|instruction_trace| instruction_trace.get(cpi_index))
163 .ok_or(InstructionError::CallDepth)?;
164 debug_assert_eq!(instruction_context.nesting_level, level);
165 Ok(instruction_context)
166 }
167
168 pub fn get_instruction_context_capacity(&self) -> usize {
170 self.instruction_context_capacity
171 }
172
173 pub fn get_instruction_context_stack_height(&self) -> usize {
176 self.instruction_stack.len()
177 }
178
179 pub fn get_current_instruction_context(&self) -> Result<&InstructionContext, InstructionError> {
181 let level = self
182 .get_instruction_context_stack_height()
183 .checked_sub(1)
184 .ok_or(InstructionError::CallDepth)?;
185 self.get_instruction_context_at(level)
186 }
187
188 pub fn push(
190 &mut self,
191 program_accounts: &[usize],
192 instruction_accounts: &[InstructionAccount],
193 instruction_data: &[u8],
194 ) -> Result<(), InstructionError> {
195 let callee_instruction_accounts_lamport_sum =
196 self.instruction_accounts_lamport_sum(instruction_accounts)?;
197 let index_in_trace = if self.instruction_stack.is_empty() {
198 debug_assert!(
199 self.instruction_trace.len() < self.number_of_instructions_at_transaction_level
200 );
201 let instruction_context = InstructionContext {
202 nesting_level: self.instruction_stack.len(),
203 instruction_accounts_lamport_sum: callee_instruction_accounts_lamport_sum,
204 program_accounts: program_accounts.to_vec(),
205 instruction_accounts: instruction_accounts.to_vec(),
206 instruction_data: instruction_data.to_vec(),
207 };
208 self.instruction_trace.push(vec![instruction_context]);
209 self.instruction_trace.len().saturating_sub(1)
210 } else {
211 if self.is_early_verification_of_account_modifications_enabled() {
212 let caller_instruction_context = self.get_current_instruction_context()?;
213 let original_caller_instruction_accounts_lamport_sum =
214 caller_instruction_context.instruction_accounts_lamport_sum;
215 let current_caller_instruction_accounts_lamport_sum = self
216 .instruction_accounts_lamport_sum(
217 &caller_instruction_context.instruction_accounts,
218 )?;
219 if original_caller_instruction_accounts_lamport_sum
220 != current_caller_instruction_accounts_lamport_sum
221 {
222 return Err(InstructionError::UnbalancedInstruction);
223 }
224 }
225 if let Some(instruction_trace) = self.instruction_trace.last_mut() {
226 let instruction_context = InstructionContext {
227 nesting_level: self.instruction_stack.len(),
228 instruction_accounts_lamport_sum: callee_instruction_accounts_lamport_sum,
229 program_accounts: program_accounts.to_vec(),
230 instruction_accounts: instruction_accounts.to_vec(),
231 instruction_data: instruction_data.to_vec(),
232 };
233 instruction_trace.push(instruction_context);
234 instruction_trace.len().saturating_sub(1)
235 } else {
236 return Err(InstructionError::CallDepth);
237 }
238 };
239 if self.instruction_stack.len() >= self.instruction_context_capacity {
240 return Err(InstructionError::CallDepth);
241 }
242 self.instruction_stack.push(index_in_trace);
243 Ok(())
244 }
245
246 pub fn pop(&mut self) -> Result<(), InstructionError> {
248 if self.instruction_stack.is_empty() {
249 return Err(InstructionError::CallDepth);
250 }
251 let detected_an_unbalanced_instruction = if self
253 .is_early_verification_of_account_modifications_enabled()
254 {
255 self.get_current_instruction_context()
256 .and_then(|instruction_context| {
257 for account_index in instruction_context.program_accounts.iter() {
259 self.get_account_at_index(*account_index)?
260 .try_borrow_mut()
261 .map_err(|_| InstructionError::AccountBorrowOutstanding)?;
262 }
263 self.instruction_accounts_lamport_sum(&instruction_context.instruction_accounts)
264 .map(|instruction_accounts_lamport_sum| {
265 instruction_context.instruction_accounts_lamport_sum
266 != instruction_accounts_lamport_sum
267 })
268 })
269 } else {
270 Ok(false)
271 };
272 self.instruction_stack.pop();
274 if detected_an_unbalanced_instruction? {
275 Err(InstructionError::UnbalancedInstruction)
276 } else {
277 Ok(())
278 }
279 }
280
281 pub fn get_return_data(&self) -> (&Pubkey, &[u8]) {
283 (&self.return_data.program_id, &self.return_data.data)
284 }
285
286 pub fn set_return_data(
288 &mut self,
289 program_id: Pubkey,
290 data: Vec<u8>,
291 ) -> Result<(), InstructionError> {
292 self.return_data = TransactionReturnData { program_id, data };
293 Ok(())
294 }
295
296 pub fn get_instruction_trace(&self) -> &InstructionTrace {
298 &self.instruction_trace
299 }
300
301 fn instruction_accounts_lamport_sum(
303 &self,
304 instruction_accounts: &[InstructionAccount],
305 ) -> Result<u128, InstructionError> {
306 if !self.is_early_verification_of_account_modifications_enabled() {
307 return Ok(0);
308 }
309 let mut instruction_accounts_lamport_sum: u128 = 0;
310 for (instruction_account_index, instruction_account) in
311 instruction_accounts.iter().enumerate()
312 {
313 if instruction_account_index != instruction_account.index_in_callee {
314 continue; }
316 instruction_accounts_lamport_sum = (self
317 .get_account_at_index(instruction_account.index_in_transaction)?
318 .try_borrow()
319 .map_err(|_| InstructionError::AccountBorrowOutstanding)?
320 .lamports() as u128)
321 .checked_add(instruction_accounts_lamport_sum)
322 .ok_or(InstructionError::ArithmeticOverflow)?;
323 }
324 Ok(instruction_accounts_lamport_sum)
325 }
326
327 pub fn accounts_resize_delta(&self) -> Result<i64, InstructionError> {
329 self.accounts_resize_delta
330 .try_borrow()
331 .map_err(|_| InstructionError::GenericError)
332 .map(|value_ref| *value_ref)
333 }
334}
335
336#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
338pub struct TransactionReturnData {
339 pub program_id: Pubkey,
340 pub data: Vec<u8>,
341}
342
343pub type InstructionTrace = Vec<Vec<InstructionContext>>;
345
346#[derive(Debug, Clone)]
350pub struct InstructionContext {
351 nesting_level: usize,
352 instruction_accounts_lamport_sum: u128,
353 program_accounts: Vec<usize>,
354 instruction_accounts: Vec<InstructionAccount>,
355 instruction_data: Vec<u8>,
356}
357
358impl InstructionContext {
359 pub fn new(
361 nesting_level: usize,
362 instruction_accounts_lamport_sum: u128,
363 program_accounts: &[usize],
364 instruction_accounts: &[InstructionAccount],
365 instruction_data: &[u8],
366 ) -> Self {
367 InstructionContext {
368 nesting_level,
369 instruction_accounts_lamport_sum,
370 program_accounts: program_accounts.to_vec(),
371 instruction_accounts: instruction_accounts.to_vec(),
372 instruction_data: instruction_data.to_vec(),
373 }
374 }
375
376 pub fn get_stack_height(&self) -> usize {
380 self.nesting_level.saturating_add(1)
381 }
382
383 pub fn get_number_of_program_accounts(&self) -> usize {
385 self.program_accounts.len()
386 }
387
388 pub fn get_number_of_instruction_accounts(&self) -> usize {
390 self.instruction_accounts.len()
391 }
392
393 pub fn check_number_of_instruction_accounts(
395 &self,
396 expected_at_least: usize,
397 ) -> Result<(), InstructionError> {
398 if self.get_number_of_instruction_accounts() < expected_at_least {
399 Err(InstructionError::NotEnoughAccountKeys)
400 } else {
401 Ok(())
402 }
403 }
404
405 pub fn get_instruction_data(&self) -> &[u8] {
407 &self.instruction_data
408 }
409
410 pub fn find_index_of_program_account(
412 &self,
413 transaction_context: &TransactionContext,
414 pubkey: &Pubkey,
415 ) -> Option<usize> {
416 self.program_accounts
417 .iter()
418 .position(|index_in_transaction| {
419 &transaction_context.account_keys[*index_in_transaction] == pubkey
420 })
421 }
422
423 pub fn find_index_of_instruction_account(
425 &self,
426 transaction_context: &TransactionContext,
427 pubkey: &Pubkey,
428 ) -> Option<usize> {
429 self.instruction_accounts
430 .iter()
431 .position(|instruction_account| {
432 &transaction_context.account_keys[instruction_account.index_in_transaction]
433 == pubkey
434 })
435 }
436
437 pub fn get_index_of_program_account_in_transaction(
439 &self,
440 program_account_index: usize,
441 ) -> Result<usize, InstructionError> {
442 Ok(*self
443 .program_accounts
444 .get(program_account_index)
445 .ok_or(InstructionError::NotEnoughAccountKeys)?)
446 }
447
448 pub fn get_index_of_instruction_account_in_transaction(
450 &self,
451 instruction_account_index: usize,
452 ) -> Result<usize, InstructionError> {
453 Ok(self
454 .instruction_accounts
455 .get(instruction_account_index)
456 .ok_or(InstructionError::NotEnoughAccountKeys)?
457 .index_in_transaction)
458 }
459
460 pub fn is_instruction_account_duplicate(
463 &self,
464 instruction_account_index: usize,
465 ) -> Result<Option<usize>, InstructionError> {
466 let index_in_callee = self
467 .instruction_accounts
468 .get(instruction_account_index)
469 .ok_or(InstructionError::NotEnoughAccountKeys)?
470 .index_in_callee;
471 Ok(if index_in_callee == instruction_account_index {
472 None
473 } else {
474 Some(index_in_callee)
475 })
476 }
477
478 pub fn get_last_program_key<'a, 'b: 'a>(
480 &'a self,
481 transaction_context: &'b TransactionContext,
482 ) -> Result<&'b Pubkey, InstructionError> {
483 let result = self
484 .get_index_of_program_account_in_transaction(
485 self.program_accounts.len().saturating_sub(1),
486 )
487 .and_then(|index_in_transaction| {
488 transaction_context.get_key_of_account_at_index(index_in_transaction)
489 });
490 debug_assert!(result.is_ok());
491 result
492 }
493
494 fn try_borrow_account<'a, 'b: 'a>(
495 &'a self,
496 transaction_context: &'b TransactionContext,
497 index_in_transaction: usize,
498 index_in_instruction: usize,
499 ) -> Result<BorrowedAccount<'a>, InstructionError> {
500 let account = transaction_context
501 .accounts
502 .get(index_in_transaction)
503 .ok_or(InstructionError::MissingAccount)?
504 .try_borrow_mut()
505 .map_err(|_| InstructionError::AccountBorrowFailed)?;
506 Ok(BorrowedAccount {
507 transaction_context,
508 instruction_context: self,
509 index_in_transaction,
510 index_in_instruction,
511 account,
512 })
513 }
514
515 pub fn try_borrow_last_program_account<'a, 'b: 'a>(
517 &'a self,
518 transaction_context: &'b TransactionContext,
519 ) -> Result<BorrowedAccount<'a>, InstructionError> {
520 let result = self.try_borrow_program_account(
521 transaction_context,
522 self.program_accounts.len().saturating_sub(1),
523 );
524 debug_assert!(result.is_ok());
525 result
526 }
527
528 pub fn try_borrow_program_account<'a, 'b: 'a>(
530 &'a self,
531 transaction_context: &'b TransactionContext,
532 program_account_index: usize,
533 ) -> Result<BorrowedAccount<'a>, InstructionError> {
534 let index_in_transaction =
535 self.get_index_of_program_account_in_transaction(program_account_index)?;
536 self.try_borrow_account(
537 transaction_context,
538 index_in_transaction,
539 program_account_index,
540 )
541 }
542
543 pub fn try_borrow_instruction_account<'a, 'b: 'a>(
545 &'a self,
546 transaction_context: &'b TransactionContext,
547 instruction_account_index: usize,
548 ) -> Result<BorrowedAccount<'a>, InstructionError> {
549 let index_in_transaction =
550 self.get_index_of_instruction_account_in_transaction(instruction_account_index)?;
551 self.try_borrow_account(
552 transaction_context,
553 index_in_transaction,
554 self.program_accounts
555 .len()
556 .saturating_add(instruction_account_index),
557 )
558 }
559
560 pub fn is_instruction_account_signer(
562 &self,
563 instruction_account_index: usize,
564 ) -> Result<bool, InstructionError> {
565 Ok(self
566 .instruction_accounts
567 .get(instruction_account_index)
568 .ok_or(InstructionError::MissingAccount)?
569 .is_signer)
570 }
571
572 pub fn is_instruction_account_writable(
574 &self,
575 instruction_account_index: usize,
576 ) -> Result<bool, InstructionError> {
577 Ok(self
578 .instruction_accounts
579 .get(instruction_account_index)
580 .ok_or(InstructionError::MissingAccount)?
581 .is_writable)
582 }
583
584 pub fn get_signers(&self, transaction_context: &TransactionContext) -> HashSet<Pubkey> {
586 let mut result = HashSet::new();
587 for instruction_account in self.instruction_accounts.iter() {
588 if instruction_account.is_signer {
589 result.insert(
590 transaction_context.account_keys[instruction_account.index_in_transaction],
591 );
592 }
593 }
594 result
595 }
596}
597
598#[derive(Debug)]
600pub struct BorrowedAccount<'a> {
601 transaction_context: &'a TransactionContext,
602 instruction_context: &'a InstructionContext,
603 index_in_transaction: usize,
604 index_in_instruction: usize,
605 account: RefMut<'a, AccountSharedData>,
606}
607
608impl<'a> BorrowedAccount<'a> {
609 pub fn get_index_in_transaction(&self) -> usize {
611 self.index_in_transaction
612 }
613
614 pub fn get_key(&self) -> &Pubkey {
616 &self.transaction_context.account_keys[self.index_in_transaction]
617 }
618
619 pub fn get_owner(&self) -> &Pubkey {
621 self.account.owner()
622 }
623
624 pub fn set_owner(&mut self, pubkey: &[u8]) -> Result<(), InstructionError> {
626 if self
627 .transaction_context
628 .is_early_verification_of_account_modifications_enabled()
629 {
630 if !self.is_owned_by_current_program() {
632 return Err(InstructionError::ModifiedProgramId);
633 }
634 if !self.is_writable() {
636 return Err(InstructionError::ModifiedProgramId);
637 }
638 if self.is_executable() {
640 return Err(InstructionError::ModifiedProgramId);
641 }
642 if !is_zeroed(self.get_data()) {
644 return Err(InstructionError::ModifiedProgramId);
645 }
646 if self.get_owner().to_bytes() == pubkey {
648 return Ok(());
649 }
650 self.touch()?;
651 }
652 self.account.copy_into_owner_from_slice(pubkey);
653 Ok(())
654 }
655
656 pub fn get_lamports(&self) -> u64 {
658 self.account.lamports()
659 }
660
661 pub fn set_lamports(&mut self, lamports: u64) -> Result<(), InstructionError> {
663 if self
664 .transaction_context
665 .is_early_verification_of_account_modifications_enabled()
666 {
667 if !self.is_owned_by_current_program() && lamports < self.get_lamports() {
669 return Err(InstructionError::ExternalAccountLamportSpend);
670 }
671 if !self.is_writable() {
673 return Err(InstructionError::ReadonlyLamportChange);
674 }
675 if self.is_executable() {
677 return Err(InstructionError::ExecutableLamportChange);
678 }
679 if self.get_lamports() == lamports {
681 return Ok(());
682 }
683 self.touch()?;
684 }
685 self.account.set_lamports(lamports);
686 Ok(())
687 }
688
689 pub fn checked_add_lamports(&mut self, lamports: u64) -> Result<(), InstructionError> {
691 self.set_lamports(
692 self.get_lamports()
693 .checked_add(lamports)
694 .ok_or(InstructionError::ArithmeticOverflow)?,
695 )
696 }
697
698 pub fn checked_sub_lamports(&mut self, lamports: u64) -> Result<(), InstructionError> {
700 self.set_lamports(
701 self.get_lamports()
702 .checked_sub(lamports)
703 .ok_or(InstructionError::ArithmeticOverflow)?,
704 )
705 }
706
707 pub fn get_data(&self) -> &[u8] {
709 self.account.data()
710 }
711
712 pub fn get_data_mut(&mut self) -> Result<&mut [u8], InstructionError> {
714 self.can_data_be_changed()?;
715 self.touch()?;
716 Ok(self.account.data_as_mut_slice())
717 }
718
719 pub fn set_data(&mut self, data: &[u8]) -> Result<(), InstructionError> {
721 self.can_data_be_resized(data.len())?;
722 self.can_data_be_changed()?;
723 self.touch()?;
724 if data.len() == self.account.data().len() {
725 self.account.data_as_mut_slice().copy_from_slice(data);
726 } else {
727 let mut accounts_resize_delta = self
728 .transaction_context
729 .accounts_resize_delta
730 .try_borrow_mut()
731 .map_err(|_| InstructionError::GenericError)?;
732 *accounts_resize_delta = accounts_resize_delta
733 .saturating_add((data.len() as i64).saturating_sub(self.get_data().len() as i64));
734 self.account.set_data_from_slice(data);
735 }
736 Ok(())
737 }
738
739 pub fn set_data_length(&mut self, new_length: usize) -> Result<(), InstructionError> {
743 self.can_data_be_resized(new_length)?;
744 self.can_data_be_changed()?;
745 if self.get_data().len() == new_length {
747 return Ok(());
748 }
749 self.touch()?;
750 let mut accounts_resize_delta = self
751 .transaction_context
752 .accounts_resize_delta
753 .try_borrow_mut()
754 .map_err(|_| InstructionError::GenericError)?;
755 *accounts_resize_delta = accounts_resize_delta
756 .saturating_add((new_length as i64).saturating_sub(self.get_data().len() as i64));
757 self.account.data_mut().resize(new_length, 0);
758 Ok(())
759 }
760
761 pub fn get_state<T: serde::de::DeserializeOwned>(&self) -> Result<T, InstructionError> {
763 self.account
764 .deserialize_data()
765 .map_err(|_| InstructionError::InvalidAccountData)
766 }
767
768 pub fn set_state<T: serde::Serialize>(&mut self, state: &T) -> Result<(), InstructionError> {
770 let data = self.get_data_mut()?;
771 let serialized_size =
772 bincode::serialized_size(state).map_err(|_| InstructionError::GenericError)?;
773 if serialized_size > data.len() as u64 {
774 return Err(InstructionError::AccountDataTooSmall);
775 }
776 bincode::serialize_into(&mut *data, state).map_err(|_| InstructionError::GenericError)?;
777 Ok(())
778 }
779
780 pub fn is_executable(&self) -> bool {
782 self.account.executable()
783 }
784
785 pub fn set_executable(&mut self, is_executable: bool) -> Result<(), InstructionError> {
787 if let Some(rent) = self.transaction_context.rent {
788 if !rent.is_exempt(self.get_lamports(), self.get_data().len()) {
790 return Err(InstructionError::ExecutableAccountNotRentExempt);
791 }
792 if !self.is_owned_by_current_program() {
794 return Err(InstructionError::ExecutableModified);
795 }
796 if !self.is_writable() {
798 return Err(InstructionError::ExecutableModified);
799 }
800 if self.is_executable() && !is_executable {
802 return Err(InstructionError::ExecutableModified);
803 }
804 if self.is_executable() == is_executable {
806 return Ok(());
807 }
808 self.touch()?;
809 }
810 self.account.set_executable(is_executable);
811 Ok(())
812 }
813
814 pub fn get_rent_epoch(&self) -> u64 {
816 self.account.rent_epoch()
817 }
818
819 pub fn is_signer(&self) -> bool {
821 if self.index_in_instruction < self.instruction_context.program_accounts.len() {
822 return false;
823 }
824 self.instruction_context
825 .is_instruction_account_signer(
826 self.index_in_instruction
827 .saturating_sub(self.instruction_context.program_accounts.len()),
828 )
829 .unwrap_or_default()
830 }
831
832 pub fn is_writable(&self) -> bool {
834 if self.index_in_instruction < self.instruction_context.program_accounts.len() {
835 return false;
836 }
837 self.instruction_context
838 .is_instruction_account_writable(
839 self.index_in_instruction
840 .saturating_sub(self.instruction_context.program_accounts.len()),
841 )
842 .unwrap_or_default()
843 }
844
845 pub fn is_owned_by_current_program(&self) -> bool {
847 self.instruction_context
848 .get_last_program_key(self.transaction_context)
849 .map(|key| key == self.get_owner())
850 .unwrap_or_default()
851 }
852
853 pub fn can_data_be_changed(&self) -> Result<(), InstructionError> {
855 if !self
856 .transaction_context
857 .is_early_verification_of_account_modifications_enabled()
858 {
859 return Ok(());
860 }
861 if self.is_executable() {
863 return Err(InstructionError::ExecutableDataModified);
864 }
865 if !self.is_writable() {
867 return Err(InstructionError::ReadonlyDataModified);
868 }
869 if !self.is_owned_by_current_program() {
871 return Err(InstructionError::ExternalAccountDataModified);
872 }
873 Ok(())
874 }
875
876 pub fn can_data_be_resized(&self, new_length: usize) -> Result<(), InstructionError> {
878 if !self
879 .transaction_context
880 .is_early_verification_of_account_modifications_enabled()
881 {
882 return Ok(());
883 }
884 if new_length != self.get_data().len() && !self.is_owned_by_current_program() {
886 return Err(InstructionError::AccountDataSizeChanged);
887 }
888 if new_length > MAX_PERMITTED_DATA_LENGTH as usize {
890 return Err(InstructionError::InvalidRealloc);
891 }
892 Ok(())
893 }
894
895 fn touch(&self) -> Result<(), InstructionError> {
896 if self
897 .transaction_context
898 .is_early_verification_of_account_modifications_enabled()
899 {
900 *self
901 .transaction_context
902 .account_touched_flags
903 .try_borrow_mut()
904 .map_err(|_| InstructionError::GenericError)?
905 .get_mut(self.index_in_transaction)
906 .ok_or(InstructionError::NotEnoughAccountKeys)? = true;
907 }
908 Ok(())
909 }
910}
911
912pub struct ExecutionRecord {
914 pub accounts: Vec<TransactionAccount>,
915 pub instruction_trace: InstructionTrace,
916 pub return_data: TransactionReturnData,
917 pub changed_account_count: u64,
918 pub total_size_of_all_accounts: u64,
919 pub total_size_of_touched_accounts: u64,
920 pub accounts_resize_delta: i64,
921}
922
923impl From<TransactionContext> for ExecutionRecord {
925 fn from(context: TransactionContext) -> Self {
926 let mut changed_account_count = 0u64;
927 let mut total_size_of_all_accounts = 0u64;
928 let mut total_size_of_touched_accounts = 0u64;
929 let account_touched_flags = context
930 .account_touched_flags
931 .try_borrow()
932 .expect("borrowing transaction_context.account_touched_flags failed");
933 for (index_in_transaction, was_touched) in account_touched_flags.iter().enumerate() {
934 let account_data_size = context
935 .get_account_at_index(index_in_transaction)
936 .expect("index_in_transaction out of bounds")
937 .try_borrow()
938 .expect("borrowing a transaction_context.account failed")
939 .data()
940 .len() as u64;
941 total_size_of_all_accounts =
942 total_size_of_all_accounts.saturating_add(account_data_size);
943 if *was_touched {
944 changed_account_count = changed_account_count.saturating_add(1);
945 total_size_of_touched_accounts =
946 total_size_of_touched_accounts.saturating_add(account_data_size);
947 }
948 }
949 Self {
950 accounts: Vec::from(Pin::into_inner(context.account_keys))
951 .into_iter()
952 .zip(
953 Vec::from(Pin::into_inner(context.accounts))
954 .into_iter()
955 .map(|account| account.into_inner()),
956 )
957 .collect(),
958 instruction_trace: context.instruction_trace,
959 return_data: context.return_data,
960 changed_account_count,
961 total_size_of_all_accounts,
962 total_size_of_touched_accounts,
963 accounts_resize_delta: RefCell::into_inner(context.accounts_resize_delta),
964 }
965 }
966}
967
968fn is_zeroed(buf: &[u8]) -> bool {
969 const ZEROS_LEN: usize = 1024;
970 const ZEROS: [u8; ZEROS_LEN] = [0; ZEROS_LEN];
971 let mut chunks = buf.chunks_exact(ZEROS_LEN);
972
973 #[allow(clippy::indexing_slicing)]
974 {
975 chunks.all(|chunk| chunk == &ZEROS[..])
976 && chunks.remainder() == &ZEROS[..chunks.remainder().len()]
977 }
978}