Skip to main content

solana_transaction_context/
transaction.rs

1#[cfg(not(any(target_arch = "bpf", target_arch = "sbf")))]
2use {
3    crate::{
4        IndexOfAccount, MAX_ACCOUNT_DATA_GROWTH_PER_TRANSACTION, MAX_ACCOUNT_DATA_LEN,
5        MAX_ACCOUNTS_PER_TRANSACTION,
6        instruction::{InstructionContext, InstructionFrame},
7        transaction_accounts::{KeyedAccountSharedData, TransactionAccounts},
8        vm_addresses::{
9            GUEST_INSTRUCTION_ACCOUNT_BASE_ADDRESS, GUEST_INSTRUCTION_DATA_BASE_ADDRESS,
10            GUEST_REGION_SIZE, RETURN_DATA_SCRATCHPAD,
11        },
12    },
13    solana_account::{AccountSharedData, ReadableAccount, WritableAccount},
14    solana_instruction::error::InstructionError,
15    solana_instructions_sysvar as instructions,
16    solana_rent::Rent,
17    solana_sbpf::memory_region::{AccessType, AccessViolationHandler, MemoryRegion},
18    std::{borrow::Cow, cell::Cell, rc::Rc},
19};
20use {
21    crate::{instruction_accounts::InstructionAccount, vm_slice::VmSlice},
22    solana_pubkey::Pubkey,
23};
24
25/// Used only in fn `take_instruction_trace` for deconstructing TransactionContext
26pub type InstructionTrace<'ix_data> = (
27    Vec<InstructionFrame>,
28    Vec<Box<[InstructionAccount]>>,
29    Vec<Cow<'ix_data, [u8]>>,
30);
31
32/// This data structure is shared with programs in ABIv2, providing information about the
33/// transaction metadata.
34///
35/// Modifications without a feature gate and proper versioning might break programs.
36#[repr(C)]
37#[derive(Debug)]
38struct TransactionFrame {
39    /// Pubkey of the last program to write to the return data scratchpad
40    return_data_pubkey: Pubkey,
41    return_data_scratchpad: VmSlice<u8>,
42    /// Scratchpad for programs to write CPI instruction data
43    pub cpi_data_scratchpad: VmSlice<u8>,
44    /// Scratchpad for programs to write CPI accounts
45    pub cpi_accounts_scratchpad: VmSlice<InstructionAccount>,
46    /// Index of current executing instruction
47    current_executing_instruction: u16,
48    /// Number of instructions in the instruction trace (including top level and CPIs)
49    total_number_of_instructions_in_trace: u16,
50    /// Number of CPIs in the instruction trace
51    number_of_cpis_in_trace: u16,
52    /// Number of transaction accounts
53    number_of_transaction_accounts: u16,
54}
55
56/// Loaded transaction shared between runtime and programs.
57///
58/// This context is valid for the entire duration of a transaction being processed.
59#[derive(Debug)]
60#[cfg(not(any(target_arch = "bpf", target_arch = "sbf")))]
61pub struct TransactionContext<'ix_data> {
62    pub(crate) accounts: Rc<TransactionAccounts>,
63    instruction_stack_capacity: usize,
64    instruction_trace_capacity: usize,
65    instruction_stack: Vec<usize>,
66    instruction_trace: Vec<InstructionFrame>,
67    transaction_frame: TransactionFrame,
68    return_data_bytes: Vec<u8>,
69    next_top_level_instruction_index: usize,
70    #[cfg(not(target_os = "solana"))]
71    pub(crate) rent: Rent,
72    /// This is an account deduplication map that maps index_in_transaction to index_in_instruction
73    /// Usage: dedup_map[index_in_transaction] = index_in_instruction
74    /// Each entry in `deduplication_maps` represents the deduplication map for each instruction.
75    deduplication_maps: Vec<Box<[u16]>>,
76    /// Each entry in `instruction_accounts` represents the array of accounts for each instruction.
77    instruction_accounts: Vec<Box<[InstructionAccount]>>,
78    /// Each entry in `instruction_data` represents the data for instruction at the corresponding
79    /// index.
80    instruction_data: Vec<Cow<'ix_data, [u8]>>,
81}
82
83#[cfg(not(any(target_arch = "bpf", target_arch = "sbf")))]
84impl<'ix_data> TransactionContext<'ix_data> {
85    /// Constructs a new TransactionContext
86    pub fn new(
87        transaction_accounts: Vec<KeyedAccountSharedData>,
88        rent: Rent,
89        instruction_stack_capacity: usize,
90        instruction_trace_capacity: usize,
91        number_of_top_level_instructions: usize,
92    ) -> Self {
93        let transaction_frame = TransactionFrame {
94            return_data_pubkey: Pubkey::default(),
95            return_data_scratchpad: VmSlice::new(RETURN_DATA_SCRATCHPAD, 0),
96            cpi_data_scratchpad: VmSlice::new(
97                GUEST_INSTRUCTION_DATA_BASE_ADDRESS.saturating_add(
98                    GUEST_REGION_SIZE.saturating_mul(number_of_top_level_instructions as u64),
99                ),
100                0,
101            ),
102            cpi_accounts_scratchpad: VmSlice::new(
103                GUEST_INSTRUCTION_ACCOUNT_BASE_ADDRESS.saturating_add(
104                    GUEST_REGION_SIZE.saturating_mul(number_of_top_level_instructions as u64),
105                ),
106                0,
107            ),
108            current_executing_instruction: 0,
109            total_number_of_instructions_in_trace: number_of_top_level_instructions as u16,
110            number_of_cpis_in_trace: 0,
111            number_of_transaction_accounts: transaction_accounts.len() as u16,
112        };
113
114        // We need an extra space for the placeholder, so we avoid relocations.
115        let mut instruction_trace =
116            Vec::with_capacity(instruction_trace_capacity.saturating_add(1));
117        instruction_trace.resize_with(
118            number_of_top_level_instructions.saturating_add(1),
119            InstructionFrame::default,
120        );
121
122        Self {
123            accounts: Rc::new(TransactionAccounts::new(transaction_accounts)),
124            instruction_stack_capacity,
125            instruction_trace_capacity,
126            instruction_stack: Vec::with_capacity(instruction_stack_capacity),
127            instruction_trace,
128            return_data_bytes: Vec::new(),
129            transaction_frame,
130            next_top_level_instruction_index: 0,
131            rent,
132            instruction_accounts: Vec::with_capacity(instruction_trace_capacity),
133            deduplication_maps: Vec::with_capacity(instruction_trace_capacity),
134            instruction_data: Vec::with_capacity(instruction_trace_capacity),
135        }
136    }
137
138    /// Used in mock_process_instruction
139    pub fn deconstruct_without_keys(self) -> Result<Vec<AccountSharedData>, InstructionError> {
140        if !self.instruction_stack.is_empty() {
141            return Err(InstructionError::CallDepth);
142        }
143
144        let accounts = Rc::try_unwrap(self.accounts)
145            .expect("transaction_context.accounts has unexpected outstanding refs")
146            .deconstruct_into_account_shared_data();
147
148        Ok(accounts)
149    }
150
151    pub fn accounts(&self) -> &Rc<TransactionAccounts> {
152        &self.accounts
153    }
154
155    /// Returns the total number of accounts loaded in this Transaction
156    pub fn get_number_of_accounts(&self) -> IndexOfAccount {
157        self.accounts.len() as IndexOfAccount
158    }
159
160    /// Searches for an account by its key
161    pub fn get_key_of_account_at_index(
162        &self,
163        index_in_transaction: IndexOfAccount,
164    ) -> Result<&Pubkey, InstructionError> {
165        self.accounts
166            .account_key(index_in_transaction)
167            .ok_or(InstructionError::MissingAccount)
168    }
169
170    /// Searches for an account by its key
171    pub fn find_index_of_account(&self, pubkey: &Pubkey) -> Option<IndexOfAccount> {
172        self.accounts
173            .account_keys_iter()
174            .position(|key| key == pubkey)
175            .map(|index| index as IndexOfAccount)
176    }
177
178    /// Gets the max length of the instruction trace
179    pub fn get_instruction_trace_capacity(&self) -> usize {
180        self.instruction_trace_capacity
181    }
182
183    /// Returns the instruction trace length.
184    ///
185    /// Not counting the last empty instruction which is always pre-reserved for the next instruction.
186    pub fn get_instruction_trace_length(&self) -> usize {
187        self.instruction_trace.len().saturating_sub(1)
188    }
189
190    /// Gets a view on an instruction by its index in the trace
191    pub fn get_instruction_context_at_index_in_trace(
192        &self,
193        index_in_trace: usize,
194    ) -> Result<InstructionContext<'_, '_>, InstructionError> {
195        let instruction = self
196            .instruction_trace
197            .get(index_in_trace)
198            .ok_or(InstructionError::CallDepth)?;
199
200        // These commands will return a default empty slice if we are retrieving an instruction
201        // that hasn't been configured yet.
202        let instruction_accounts = self
203            .instruction_accounts
204            .get(index_in_trace)
205            .map(|item| item.as_ref())
206            .unwrap_or_default();
207        let dedup_map = self
208            .deduplication_maps
209            .get(index_in_trace)
210            .map(|item| item.as_ref())
211            .unwrap_or_default();
212        let instruction_data = self
213            .instruction_data
214            .get(index_in_trace)
215            .map(|item| item.as_ref())
216            .unwrap_or_default();
217        Ok(InstructionContext {
218            transaction_context: self,
219            index_in_trace,
220            nesting_level: instruction.nesting_level as usize,
221            program_account_index_in_tx: instruction.program_account_index_in_tx as IndexOfAccount,
222            instruction_accounts,
223            dedup_map,
224            instruction_data,
225            index_of_caller_instruction: instruction.index_of_caller_instruction as usize,
226        })
227    }
228
229    /// Gets a view on the instruction by its nesting level in the stack
230    pub fn get_instruction_context_at_nesting_level(
231        &self,
232        nesting_level: usize,
233    ) -> Result<InstructionContext<'_, '_>, InstructionError> {
234        let index_in_trace = *self
235            .instruction_stack
236            .get(nesting_level)
237            .ok_or(InstructionError::CallDepth)?;
238        let instruction_context = self.get_instruction_context_at_index_in_trace(index_in_trace)?;
239        debug_assert_eq!(instruction_context.nesting_level, nesting_level);
240        Ok(instruction_context)
241    }
242
243    /// Gets the max height of the instruction stack
244    pub fn get_instruction_stack_capacity(&self) -> usize {
245        self.instruction_stack_capacity
246    }
247
248    /// Gets instruction stack height, top-level instructions are height
249    /// `solana_instruction::TRANSACTION_LEVEL_STACK_HEIGHT`
250    pub fn get_instruction_stack_height(&self) -> usize {
251        self.instruction_stack.len()
252    }
253
254    /// Returns the index in the instruction trace of the current executing instruction
255    pub fn get_current_instruction_index(&self) -> Result<usize, InstructionError> {
256        self.instruction_stack
257            .last()
258            .copied()
259            .ok_or(InstructionError::CallDepth)
260    }
261
262    /// Returns a view on the current instruction
263    pub fn get_current_instruction_context(
264        &self,
265    ) -> Result<InstructionContext<'_, '_>, InstructionError> {
266        let index_in_trace = self.get_current_instruction_index()?;
267        self.get_instruction_context_at_index_in_trace(index_in_trace)
268    }
269
270    /// Returns a view on the next instruction. This function assumes it has already been
271    /// configured with the correct values in `prepare_next_instruction` or
272    /// `prepare_next_top_level_instruction`
273    pub fn get_next_instruction_context(
274        &self,
275    ) -> Result<InstructionContext<'_, '_>, InstructionError> {
276        let index_in_trace = if self.instruction_stack.is_empty() {
277            self.next_top_level_instruction_index
278        } else {
279            self.instruction_trace
280                .len()
281                .checked_sub(1)
282                .ok_or(InstructionError::CallDepth)?
283        };
284        self.get_instruction_context_at_index_in_trace(index_in_trace)
285    }
286
287    /// Configures an instruction at a specific index in trace.
288    pub fn configure_instruction_at_index(
289        &mut self,
290        instruction_index: usize,
291        program_index: IndexOfAccount,
292        instruction_accounts: Vec<InstructionAccount>,
293        deduplication_map: Vec<u16>,
294        instruction_data: Cow<'ix_data, [u8]>,
295        caller_index: Option<u16>,
296    ) -> Result<(), InstructionError> {
297        debug_assert_eq!(
298            deduplication_map.len(),
299            usize::from(self.get_number_of_accounts()).min(MAX_ACCOUNTS_PER_TRANSACTION)
300        );
301
302        let instruction = self
303            .instruction_trace
304            .get_mut(instruction_index)
305            .ok_or(InstructionError::MaxInstructionTraceLengthExceeded)?;
306
307        // If we have a parent index, then we are dealing with a CPI.
308        if let Some(caller_index) = caller_index {
309            self.transaction_frame.total_number_of_instructions_in_trace = self
310                .transaction_frame
311                .total_number_of_instructions_in_trace
312                .saturating_add(1);
313            instruction.index_of_caller_instruction = caller_index;
314            let next_data_ptr = self
315                .transaction_frame
316                .cpi_data_scratchpad
317                .ptr()
318                .saturating_add(GUEST_REGION_SIZE);
319            self.transaction_frame.cpi_data_scratchpad = VmSlice::new(next_data_ptr, 0);
320            let next_accounts_ptr = self
321                .transaction_frame
322                .cpi_accounts_scratchpad
323                .ptr()
324                .saturating_add(GUEST_REGION_SIZE);
325            self.transaction_frame.cpi_accounts_scratchpad = VmSlice::new(next_accounts_ptr, 0);
326        }
327
328        instruction.program_account_index_in_tx = program_index;
329        instruction.configure_vm_slices(
330            instruction_index as u64,
331            instruction_accounts.len(),
332            instruction_data.len() as u64,
333        );
334        self.deduplication_maps
335            .push(deduplication_map.into_boxed_slice());
336        self.instruction_accounts
337            .push(instruction_accounts.into_boxed_slice());
338        self.instruction_data.push(instruction_data);
339        Ok(())
340    }
341
342    /// For tests only
343    fn deduplicate_accounts_for_tests(
344        &self,
345        instruction_accounts: &[InstructionAccount],
346    ) -> Vec<u16> {
347        let mut dedup_map = vec![
348            u16::MAX;
349            usize::from(self.get_number_of_accounts())
350                .min(MAX_ACCOUNTS_PER_TRANSACTION)
351        ];
352        for (idx, account) in instruction_accounts.iter().enumerate() {
353            let index_in_instruction = dedup_map
354                .get_mut(account.index_in_transaction as usize)
355                .unwrap();
356            if *index_in_instruction == u16::MAX {
357                *index_in_instruction = idx as u16;
358            }
359        }
360        dedup_map
361    }
362
363    /// A version of `configure_top_level_instruction` to help creating the deduplication map in tests
364    pub fn configure_top_level_instruction_for_tests(
365        &mut self,
366        program_index: IndexOfAccount,
367        instruction_accounts: Vec<InstructionAccount>,
368        instruction_data: Vec<u8>,
369    ) -> Result<(), InstructionError> {
370        debug_assert!(instruction_accounts.len() <= u16::MAX as usize);
371        let dedup_map = self.deduplicate_accounts_for_tests(&instruction_accounts);
372
373        self.configure_instruction_at_index(
374            self.next_top_level_instruction_index,
375            program_index,
376            instruction_accounts,
377            dedup_map,
378            Cow::Owned(instruction_data),
379            None,
380        )?;
381        Ok(())
382    }
383
384    /// A helper function to facilitate creating a CPI in tests
385    pub fn configure_next_cpi_for_tests(
386        &mut self,
387        program_index: IndexOfAccount,
388        instruction_accounts: Vec<InstructionAccount>,
389        instruction_data: Vec<u8>,
390    ) -> Result<(), InstructionError> {
391        debug_assert!(instruction_accounts.len() <= u16::MAX as usize);
392        let dedup_map = self.deduplicate_accounts_for_tests(&instruction_accounts);
393        let caller_index = self.get_current_instruction_index()?;
394        let cpi_index = self.get_instruction_trace_length();
395        self.configure_instruction_at_index(
396            cpi_index,
397            program_index,
398            instruction_accounts,
399            dedup_map,
400            Cow::Owned(instruction_data),
401            Some(caller_index as u16),
402        )?;
403        Ok(())
404    }
405
406    /// Pushes the next instruction
407    pub fn push(&mut self) -> Result<(), InstructionError> {
408        let nesting_level = self.get_instruction_stack_height();
409        if !self.instruction_stack.is_empty() && self.accounts.get_lamports_delta() != 0 {
410            return Err(InstructionError::UnbalancedInstruction);
411        }
412        {
413            let instruction = self
414                .instruction_trace
415                .last_mut()
416                .ok_or(InstructionError::CallDepth)?;
417            instruction.nesting_level = nesting_level as u16;
418        }
419
420        if self.number_of_called_instructions_in_trace() >= self.instruction_trace_capacity {
421            return Err(InstructionError::MaxInstructionTraceLengthExceeded);
422        }
423
424        let (index_in_trace, current_top_level_instruction) = if self.instruction_stack.is_empty() {
425            let index = self.next_top_level_instruction_index;
426            self.next_top_level_instruction_index =
427                self.next_top_level_instruction_index.saturating_add(1);
428            (index, index)
429        } else {
430            let index = self.get_instruction_trace_length();
431            self.transaction_frame.number_of_cpis_in_trace = self
432                .transaction_frame
433                .number_of_cpis_in_trace
434                .saturating_add(1);
435            self.instruction_trace.push(InstructionFrame::default());
436            (
437                index,
438                self.next_top_level_instruction_index.saturating_sub(1),
439            )
440        };
441
442        if nesting_level >= self.instruction_stack_capacity {
443            return Err(InstructionError::CallDepth);
444        }
445        self.transaction_frame.current_executing_instruction = index_in_trace as u16;
446        self.instruction_stack.push(index_in_trace);
447        if let Some(index_in_transaction) = self.find_index_of_account(&instructions::id()) {
448            let mut mut_account_ref = self.accounts.try_borrow_mut(index_in_transaction)?;
449            if mut_account_ref.owner() != &solana_sdk_ids::sysvar::id() {
450                return Err(InstructionError::InvalidAccountOwner);
451            }
452            instructions::store_current_index_checked(
453                mut_account_ref.data_as_mut_slice(),
454                current_top_level_instruction as u16,
455            )?;
456        }
457        Ok(())
458    }
459
460    /// Pops the current instruction
461    pub fn pop(&mut self) -> Result<(), InstructionError> {
462        if self.instruction_stack.is_empty() {
463            return Err(InstructionError::CallDepth);
464        }
465        // Verify (before we pop) that the total sum of all lamports in this instruction did not change
466        let detected_an_unbalanced_instruction =
467            self.get_current_instruction_context()
468                .and_then(|instruction_context| {
469                    // Verify all executable accounts have no outstanding refs
470                    self.accounts
471                        .try_borrow_mut(
472                            instruction_context.get_index_of_program_account_in_transaction()?,
473                        )
474                        .map_err(|err| {
475                            if err == InstructionError::AccountBorrowFailed {
476                                InstructionError::AccountBorrowOutstanding
477                            } else {
478                                err
479                            }
480                        })?;
481                    Ok(self.accounts.get_lamports_delta() != 0)
482                });
483        // Always pop, even if we `detected_an_unbalanced_instruction`
484        self.instruction_stack.pop();
485        if let Some(instr_idx) = self.instruction_stack.last() {
486            self.transaction_frame.current_executing_instruction = *instr_idx as u16;
487        }
488        if detected_an_unbalanced_instruction? {
489            Err(InstructionError::UnbalancedInstruction)
490        } else {
491            Ok(())
492        }
493    }
494
495    /// Gets the return data of the current instruction or any above
496    pub fn get_return_data(&self) -> (&Pubkey, &[u8]) {
497        (
498            &self.transaction_frame.return_data_pubkey,
499            &self.return_data_bytes,
500        )
501    }
502
503    /// Set the return data of the current instruction
504    pub fn set_return_data(
505        &mut self,
506        program_id: Pubkey,
507        data: Vec<u8>,
508    ) -> Result<(), InstructionError> {
509        self.transaction_frame.return_data_pubkey = program_id;
510        self.transaction_frame
511            .return_data_scratchpad
512            .set_len(data.len() as u64);
513        self.return_data_bytes = data;
514        Ok(())
515    }
516
517    /// Returns a new account data write access handler
518    pub fn access_violation_handler(
519        &self,
520        virtual_address_space_adjustments: bool,
521        account_data_direct_mapping: bool,
522    ) -> AccessViolationHandler {
523        let accounts = Rc::clone(&self.accounts);
524        Box::new(
525            move |region: &mut MemoryRegion,
526                  address_space_reserved_for_account: u64,
527                  access_type: AccessType,
528                  vm_addr: u64,
529                  len: u64| {
530                if access_type == AccessType::Load {
531                    return;
532                }
533                let Some(index_in_transaction) = region.access_violation_handler_payload else {
534                    // This region is not a writable account.
535                    return;
536                };
537                let region_vm_addr_start = region.vm_addr_range().start;
538                let requested_length = vm_addr
539                    .saturating_add(len)
540                    .saturating_sub(region_vm_addr_start)
541                    as usize;
542                if requested_length > address_space_reserved_for_account as usize {
543                    // Requested access goes further than the account region.
544                    return;
545                }
546
547                // The four calls below can't really fail. If they fail because of a bug,
548                // whatever is writing will trigger an EbpfError::AccessViolation like
549                // if the region was readonly, and the transaction will fail gracefully.
550                let Ok(mut account) = accounts.try_borrow_mut(index_in_transaction) else {
551                    debug_assert!(false);
552                    return;
553                };
554                if accounts.touch(index_in_transaction).is_err() {
555                    debug_assert!(false);
556                    return;
557                }
558
559                let remaining_allowed_growth = MAX_ACCOUNT_DATA_GROWTH_PER_TRANSACTION
560                    .saturating_sub(accounts.resize_delta())
561                    .max(0) as usize;
562
563                if requested_length > region.len() {
564                    // Realloc immediately here to fit the requested access,
565                    // then later in CPI or deserialization realloc again to the
566                    // account length the program stored in AccountInfo.
567                    let old_len = account.data().len();
568                    let new_len = (address_space_reserved_for_account as usize)
569                        .min(MAX_ACCOUNT_DATA_LEN as usize)
570                        .min(old_len.saturating_add(remaining_allowed_growth));
571                    // The last two min operations ensure the following:
572                    debug_assert!(accounts.can_data_be_resized(old_len, new_len).is_ok());
573                    if accounts
574                        .update_accounts_resize_delta(old_len, new_len)
575                        .is_err()
576                    {
577                        return;
578                    }
579
580                    account.resize(new_len, 0);
581                    let data_ptr = region.host_buffer().ptr() as *mut u8;
582                    let new_buffer = std::ptr::slice_from_raw_parts_mut(data_ptr, new_len);
583                    unsafe {
584                        // SAFETY:
585                        //
586                        // Contract from `MemoryRegion::redirect`: MemoryRegion must point to a
587                        // valid object live for the duration of this `MemoryMapping`.
588                        //
589                        // Evidence: There are two distinct cases, when the account buffer is
590                        // serialized and when the account buffer is directly mapped.
591                        // * In the serialization case we continue pointing at the same buffer as
592                        // before, and the original buffer must have satisfied the liveness
593                        // condition before.
594                        // * In the direct mapping case `account.resize` invalidates the buffer this
595                        // region has been pointing at, but this is fixed up later in the "unshare"
596                        // branch later.
597                        // * In the serialization case the section of serialized buffer has the
598                        // necessary padding after the account payload proper for resize. This
599                        // padding is a part of the originally constructed `MemoryRegion` and is
600                        // only later subsliced to not expose it before the first access to the
601                        // area (which invokes this handler.)
602                        //
603                        // Contract from `MemoryRegion::redirect`: For `MemoryRegion`s marked
604                        // writable, the host buffer must accept arbitrary bytes being overwritten
605                        // without it resulting in unsoundness.
606                        //
607                        // Evidence: The account payloads dont have any internal soundness
608                        // invariants. The buffer in the serialization case starts off and remains
609                        // writable (even though the HostBuffer might have been initially created as
610                        // immutable.) In the direct mapping case we redirect the region to the
611                        // buffer stored in the account later on.
612                        region.redirect(new_buffer);
613                    }
614                }
615
616                // Potentially unshare / make the account shared data unique (CoW logic).
617                if virtual_address_space_adjustments && account_data_direct_mapping {
618                    unsafe {
619                        // SAFETY: refer to the comment above.
620                        region.redirect(account.raw_mut_data_slice());
621                    }
622                }
623            },
624        )
625    }
626
627    /// Take ownership of the instruction trace
628    pub fn take_instruction_trace(&mut self) -> InstructionTrace<'_> {
629        // The last frame is a placeholder for the next instruction to be executed, so it
630        // is empty.
631        self.instruction_trace.pop();
632        (
633            std::mem::take(&mut self.instruction_trace),
634            std::mem::take(&mut self.instruction_accounts),
635            std::mem::take(&mut self.instruction_data),
636        )
637    }
638
639    /// Called instruction are those that the program runtime has already called into. It
640    /// encompasses instructions under execution (e.g. all nested CPIs are already called) and
641    /// finished ones.
642    ///
643    /// Top level instructions that have not yet been executed aren't considered called.
644    pub fn number_of_called_instructions_in_trace(&self) -> usize {
645        self.next_top_level_instruction_index
646            .saturating_add(self.transaction_frame.number_of_cpis_in_trace as usize)
647    }
648
649    /// Return next top level instruction to execute
650    pub fn next_top_level_instruction_index(&self) -> usize {
651        self.next_top_level_instruction_index
652    }
653
654    /// Return number of CPIs in instruction trace
655    pub fn number_of_cpis_in_trace(&self) -> usize {
656        self.transaction_frame.number_of_cpis_in_trace as usize
657    }
658}
659
660/// Return data at the end of a transaction
661#[cfg(not(any(target_arch = "bpf", target_arch = "sbf")))]
662#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
663#[cfg_attr(feature = "wincode", derive(wincode::SchemaRead, wincode::SchemaWrite))]
664#[derive(Clone, Debug, Default, PartialEq, Eq)]
665pub struct TransactionReturnData {
666    pub program_id: Pubkey,
667    pub data: Vec<u8>,
668}
669
670/// Everything that needs to be recorded from a TransactionContext after execution
671#[cfg(not(any(target_arch = "bpf", target_arch = "sbf")))]
672pub struct ExecutionRecord {
673    pub accounts: Vec<KeyedAccountSharedData>,
674    pub return_data: TransactionReturnData,
675    /// Parallel to `accounts`: whether each account was modified by the VM.
676    pub touched_flags: Box<[bool]>,
677    pub accounts_resize_delta: i64,
678}
679
680/// Used by the bank in the runtime to write back the processed accounts and recorded instructions
681#[cfg(not(any(target_arch = "bpf", target_arch = "sbf")))]
682impl From<TransactionContext<'_>> for ExecutionRecord {
683    fn from(context: TransactionContext) -> Self {
684        let (accounts, touched_flags, resize_delta) = Rc::try_unwrap(context.accounts)
685            .expect("transaction_context.accounts has unexpected outstanding refs")
686            .take();
687
688        // The flags only needed interior mutability while the VM was running.
689        // Now that we own them, unwrap the per-element `Cell`s into a plain
690        // `Box<[bool]>`. `Vec::from` reuses the box's allocation and the mapped
691        // collect reuses that same buffer in place (`Cell<bool>` and `bool` have
692        // identical layout), so no reallocation occurs.
693        let touched_flags: Box<[bool]> = Vec::from(touched_flags)
694            .into_iter()
695            .map(|flag| flag.into_inner())
696            .collect();
697
698        let return_data = TransactionReturnData {
699            program_id: context.transaction_frame.return_data_pubkey,
700            data: context.return_data_bytes,
701        };
702
703        Self {
704            accounts,
705            return_data,
706            touched_flags,
707            accounts_resize_delta: Cell::into_inner(resize_delta),
708        }
709    }
710}
711
712#[cfg(all(test, not(target_arch = "sbf"), not(target_arch = "bpf")))]
713mod tests {
714    use super::*;
715
716    #[test]
717    fn test_instructions_sysvar_store_index_checked() {
718        let build_transaction_context = |account: AccountSharedData| {
719            TransactionContext::new(
720                vec![
721                    (Pubkey::new_unique(), AccountSharedData::default()),
722                    (instructions::id(), account),
723                ],
724                Rent::default(),
725                /* max_instruction_stack_depth */ 2,
726                /* max_instruction_trace_length */ 2,
727                /* number_of_top_level_instructions */ 1,
728            )
729        };
730
731        let correct_space = 2;
732        let rent_exempt_lamports = Rent::default().minimum_balance(correct_space);
733
734        // First try it with the wrong owner.
735        let account =
736            AccountSharedData::new(rent_exempt_lamports, correct_space, &Pubkey::new_unique());
737        assert_eq!(
738            build_transaction_context(account).push(),
739            Err(InstructionError::InvalidAccountOwner),
740        );
741
742        // Now with the wrong data length.
743        let account =
744            AccountSharedData::new(rent_exempt_lamports, 0, &solana_sdk_ids::sysvar::id());
745        assert_eq!(
746            build_transaction_context(account).push(),
747            Err(InstructionError::AccountDataTooSmall),
748        );
749
750        // Finally provide the correct account setup.
751        let account = AccountSharedData::new(
752            rent_exempt_lamports,
753            correct_space,
754            &solana_sdk_ids::sysvar::id(),
755        );
756        assert_eq!(build_transaction_context(account).push(), Ok(()),);
757    }
758
759    #[test]
760    fn test_invalid_native_loader_index() {
761        let mut transaction_context = TransactionContext::new(
762            vec![(
763                Pubkey::new_unique(),
764                AccountSharedData::new(1, 1, &Pubkey::new_unique()),
765            )],
766            Rent::default(),
767            20,
768            20,
769            1,
770        );
771
772        transaction_context
773            .configure_top_level_instruction_for_tests(
774                u16::MAX,
775                vec![InstructionAccount::new(0, false, false)],
776                vec![],
777            )
778            .unwrap();
779        let instruction_context = transaction_context.get_next_instruction_context().unwrap();
780
781        let result = instruction_context.get_index_of_program_account_in_transaction();
782        assert_eq!(result, Err(InstructionError::MissingAccount));
783
784        let result = instruction_context.get_program_key();
785        assert_eq!(result, Err(InstructionError::MissingAccount));
786
787        let result = instruction_context.get_program_owner();
788        assert_eq!(result.err(), Some(InstructionError::MissingAccount));
789    }
790
791    #[test]
792    fn test_instruction_shared_items() {
793        let transaction_accounts = vec![(Pubkey::new_unique(), AccountSharedData::default()); 11];
794        let mut transaction_context =
795            TransactionContext::new(transaction_accounts, Rent::default(), 20, 20, 3);
796
797        let instruction_accounts_1 = vec![
798            InstructionAccount::new(0, false, true),
799            InstructionAccount::new(3, true, false),
800        ];
801        transaction_context
802            .configure_top_level_instruction_for_tests(
803                1,
804                instruction_accounts_1.clone(),
805                vec![1, 2, 3, 4],
806            )
807            .unwrap();
808        transaction_context.push().unwrap();
809
810        let instruction_accounts_2 = vec![
811            InstructionAccount::new(0, false, true),
812            InstructionAccount::new(3, true, false),
813            InstructionAccount::new(5, false, false),
814        ];
815        transaction_context
816            .configure_top_level_instruction_for_tests(
817                1,
818                instruction_accounts_2.clone(),
819                vec![5, 6, 7, 8, 9],
820            )
821            .unwrap();
822        transaction_context.push().unwrap();
823
824        let instruction_accounts_3 = vec![
825            InstructionAccount::new(0, false, true),
826            InstructionAccount::new(3, true, false),
827            InstructionAccount::new(5, false, false),
828            InstructionAccount::new(3, false, false),
829            InstructionAccount::new(10, false, false),
830        ];
831        transaction_context
832            .configure_top_level_instruction_for_tests(
833                1,
834                instruction_accounts_3.clone(),
835                vec![10, 11],
836            )
837            .unwrap();
838        transaction_context.push().unwrap();
839
840        let first_ix_context = transaction_context
841            .get_instruction_context_at_index_in_trace(0)
842            .unwrap();
843        assert_eq!(
844            instruction_accounts_1.as_slice(),
845            first_ix_context.instruction_accounts
846        );
847        assert_eq!(
848            *first_ix_context.instruction_data,
849            **transaction_context.instruction_data.first().unwrap()
850        );
851        for (idx_in_ix, acc) in instruction_accounts_1.iter().enumerate() {
852            assert_eq!(
853                *first_ix_context
854                    .dedup_map
855                    .get(acc.index_in_transaction as usize)
856                    .unwrap(),
857                idx_in_ix as u16
858            );
859        }
860
861        let second_ix_context = transaction_context
862            .get_instruction_context_at_index_in_trace(1)
863            .unwrap();
864        assert_eq!(
865            instruction_accounts_2.as_slice(),
866            second_ix_context.instruction_accounts
867        );
868        assert_eq!(
869            *second_ix_context.instruction_data,
870            **transaction_context.instruction_data.get(1).unwrap()
871        );
872        for (idx_in_ix, acc) in instruction_accounts_2.iter().enumerate() {
873            assert_eq!(
874                *second_ix_context
875                    .dedup_map
876                    .get(acc.index_in_transaction as usize)
877                    .unwrap(),
878                idx_in_ix as u16
879            );
880        }
881
882        let third_ix_context = transaction_context
883            .get_instruction_context_at_index_in_trace(2)
884            .unwrap();
885        assert_eq!(
886            instruction_accounts_3.as_slice(),
887            third_ix_context.instruction_accounts
888        );
889        assert_eq!(
890            *third_ix_context.instruction_data,
891            **transaction_context.instruction_data.get(2).unwrap()
892        );
893        for (idx_in_ix, acc) in instruction_accounts_3.iter().enumerate() {
894            if idx_in_ix == 3 {
895                assert_eq!(
896                    *third_ix_context
897                        .dedup_map
898                        .get(acc.index_in_transaction as usize)
899                        .unwrap(),
900                    1
901                );
902            } else {
903                assert_eq!(
904                    *third_ix_context
905                        .dedup_map
906                        .get(acc.index_in_transaction as usize)
907                        .unwrap(),
908                    idx_in_ix as u16
909                );
910            }
911        }
912    }
913
914    #[test]
915    fn test_number_of_instructions() {
916        let transaction_accounts = vec![(Pubkey::new_unique(), AccountSharedData::default()); 3];
917        let mut transaction_context =
918            TransactionContext::new(transaction_accounts, Rent::default(), 20, 20, 2);
919        assert_eq!(
920            transaction_context
921                .transaction_frame
922                .number_of_cpis_in_trace,
923            0
924        );
925
926        // Instruction #0
927        transaction_context
928            .configure_instruction_at_index(
929                0,
930                0,
931                vec![InstructionAccount::new(1, false, false)],
932                vec![0; 3],
933                Vec::new().into(),
934                None,
935            )
936            .unwrap();
937
938        // Instruction #1
939        transaction_context
940            .configure_instruction_at_index(
941                1,
942                0,
943                vec![InstructionAccount::new(1, false, false)],
944                vec![0; 3],
945                Vec::new().into(),
946                None,
947            )
948            .unwrap();
949
950        // Executing instruction #0
951        transaction_context.push().unwrap();
952        assert_eq!(
953            transaction_context
954                .transaction_frame
955                .current_executing_instruction,
956            0
957        );
958        assert_eq!(
959            transaction_context.number_of_called_instructions_in_trace(),
960            1
961        );
962
963        assert_eq!(
964            transaction_context
965                .transaction_frame
966                .total_number_of_instructions_in_trace,
967            2
968        );
969
970        assert_eq!(
971            transaction_context
972                .transaction_frame
973                .number_of_cpis_in_trace,
974            0
975        );
976
977        assert_eq!(
978            transaction_context
979                .transaction_frame
980                .cpi_data_scratchpad
981                .ptr(),
982            GUEST_INSTRUCTION_DATA_BASE_ADDRESS.saturating_add(GUEST_REGION_SIZE.saturating_mul(2))
983        );
984        assert_eq!(
985            transaction_context
986                .transaction_frame
987                .cpi_data_scratchpad
988                .len(),
989            0,
990        );
991        assert_eq!(
992            transaction_context
993                .transaction_frame
994                .cpi_accounts_scratchpad
995                .ptr(),
996            GUEST_INSTRUCTION_ACCOUNT_BASE_ADDRESS
997                .saturating_add(GUEST_REGION_SIZE.saturating_mul(2))
998        );
999        assert_eq!(
1000            transaction_context
1001                .transaction_frame
1002                .cpi_data_scratchpad
1003                .len(),
1004            0,
1005        );
1006
1007        assert_eq!(
1008            transaction_context.number_of_called_instructions_in_trace(),
1009            1
1010        );
1011
1012        // Instruction #0 does a CPI.
1013        transaction_context
1014            .configure_next_cpi_for_tests(
1015                0,
1016                vec![InstructionAccount::new(2, false, true)],
1017                Vec::new(),
1018            )
1019            .unwrap();
1020
1021        transaction_context.push().unwrap();
1022        assert_eq!(
1023            transaction_context
1024                .transaction_frame
1025                .current_executing_instruction,
1026            2
1027        );
1028
1029        assert_eq!(
1030            transaction_context
1031                .transaction_frame
1032                .total_number_of_instructions_in_trace,
1033            3
1034        );
1035        assert_eq!(
1036            transaction_context
1037                .transaction_frame
1038                .number_of_cpis_in_trace,
1039            1
1040        );
1041        assert_eq!(
1042            transaction_context.number_of_called_instructions_in_trace(),
1043            2
1044        );
1045
1046        assert_eq!(
1047            transaction_context
1048                .transaction_frame
1049                .cpi_data_scratchpad
1050                .ptr(),
1051            GUEST_INSTRUCTION_DATA_BASE_ADDRESS.saturating_add(GUEST_REGION_SIZE.saturating_mul(3))
1052        );
1053        assert_eq!(
1054            transaction_context
1055                .transaction_frame
1056                .cpi_accounts_scratchpad
1057                .ptr(),
1058            GUEST_INSTRUCTION_ACCOUNT_BASE_ADDRESS
1059                .saturating_add(GUEST_REGION_SIZE.saturating_mul(3))
1060        );
1061
1062        // A nested CPI
1063        transaction_context
1064            .configure_next_cpi_for_tests(
1065                0,
1066                vec![InstructionAccount::new(2, false, true)],
1067                Vec::new(),
1068            )
1069            .unwrap();
1070
1071        transaction_context.push().unwrap();
1072        assert_eq!(
1073            transaction_context
1074                .transaction_frame
1075                .current_executing_instruction,
1076            3
1077        );
1078
1079        assert_eq!(
1080            transaction_context
1081                .transaction_frame
1082                .total_number_of_instructions_in_trace,
1083            4
1084        );
1085
1086        assert_eq!(
1087            transaction_context
1088                .transaction_frame
1089                .cpi_data_scratchpad
1090                .ptr(),
1091            GUEST_INSTRUCTION_DATA_BASE_ADDRESS.saturating_add(GUEST_REGION_SIZE.saturating_mul(4))
1092        );
1093        assert_eq!(
1094            transaction_context
1095                .transaction_frame
1096                .cpi_accounts_scratchpad
1097                .ptr(),
1098            GUEST_INSTRUCTION_ACCOUNT_BASE_ADDRESS
1099                .saturating_add(GUEST_REGION_SIZE.saturating_mul(4))
1100        );
1101
1102        assert_eq!(
1103            transaction_context
1104                .transaction_frame
1105                .number_of_cpis_in_trace,
1106            2
1107        );
1108
1109        assert_eq!(
1110            transaction_context.number_of_called_instructions_in_trace(),
1111            3
1112        );
1113        // Return from nested CPI
1114        transaction_context.pop().unwrap();
1115        assert_eq!(
1116            transaction_context.number_of_called_instructions_in_trace(),
1117            3
1118        );
1119
1120        assert_eq!(
1121            transaction_context
1122                .transaction_frame
1123                .total_number_of_instructions_in_trace,
1124            4
1125        );
1126        assert_eq!(
1127            transaction_context
1128                .transaction_frame
1129                .number_of_cpis_in_trace,
1130            2,
1131        );
1132        assert_eq!(
1133            transaction_context
1134                .transaction_frame
1135                .current_executing_instruction,
1136            2
1137        );
1138
1139        // A second nested CPI
1140        transaction_context
1141            .configure_next_cpi_for_tests(
1142                0,
1143                vec![InstructionAccount::new(2, false, true)],
1144                Vec::new(),
1145            )
1146            .unwrap();
1147
1148        transaction_context.push().unwrap();
1149        assert_eq!(
1150            transaction_context
1151                .transaction_frame
1152                .current_executing_instruction,
1153            4
1154        );
1155
1156        assert_eq!(
1157            transaction_context
1158                .transaction_frame
1159                .total_number_of_instructions_in_trace,
1160            5
1161        );
1162
1163        assert_eq!(
1164            transaction_context
1165                .transaction_frame
1166                .cpi_data_scratchpad
1167                .ptr(),
1168            GUEST_INSTRUCTION_DATA_BASE_ADDRESS.saturating_add(GUEST_REGION_SIZE.saturating_mul(5))
1169        );
1170        assert_eq!(
1171            transaction_context
1172                .transaction_frame
1173                .cpi_accounts_scratchpad
1174                .ptr(),
1175            GUEST_INSTRUCTION_ACCOUNT_BASE_ADDRESS
1176                .saturating_add(GUEST_REGION_SIZE.saturating_mul(5))
1177        );
1178
1179        assert_eq!(
1180            transaction_context
1181                .transaction_frame
1182                .number_of_cpis_in_trace,
1183            3
1184        );
1185        assert_eq!(
1186            transaction_context.number_of_called_instructions_in_trace(),
1187            4
1188        );
1189
1190        // Return from second nested CPI
1191        transaction_context.pop().unwrap();
1192
1193        assert_eq!(
1194            transaction_context
1195                .transaction_frame
1196                .current_executing_instruction,
1197            2
1198        );
1199
1200        assert_eq!(
1201            transaction_context
1202                .transaction_frame
1203                .total_number_of_instructions_in_trace,
1204            5
1205        );
1206
1207        assert_eq!(
1208            transaction_context
1209                .transaction_frame
1210                .cpi_data_scratchpad
1211                .ptr(),
1212            GUEST_INSTRUCTION_DATA_BASE_ADDRESS.saturating_add(GUEST_REGION_SIZE.saturating_mul(5))
1213        );
1214        assert_eq!(
1215            transaction_context
1216                .transaction_frame
1217                .cpi_accounts_scratchpad
1218                .ptr(),
1219            GUEST_INSTRUCTION_ACCOUNT_BASE_ADDRESS
1220                .saturating_add(GUEST_REGION_SIZE.saturating_mul(5))
1221        );
1222
1223        assert_eq!(
1224            transaction_context
1225                .transaction_frame
1226                .number_of_cpis_in_trace,
1227            3
1228        );
1229
1230        // Return from first CPI
1231        transaction_context.pop().unwrap();
1232        assert_eq!(
1233            transaction_context.number_of_called_instructions_in_trace(),
1234            4
1235        );
1236
1237        assert_eq!(
1238            transaction_context
1239                .transaction_frame
1240                .current_executing_instruction,
1241            0
1242        );
1243
1244        assert_eq!(
1245            transaction_context
1246                .transaction_frame
1247                .total_number_of_instructions_in_trace,
1248            5
1249        );
1250
1251        assert_eq!(
1252            transaction_context
1253                .transaction_frame
1254                .cpi_data_scratchpad
1255                .ptr(),
1256            GUEST_INSTRUCTION_DATA_BASE_ADDRESS.saturating_add(GUEST_REGION_SIZE.saturating_mul(5))
1257        );
1258        assert_eq!(
1259            transaction_context
1260                .transaction_frame
1261                .cpi_accounts_scratchpad
1262                .ptr(),
1263            GUEST_INSTRUCTION_ACCOUNT_BASE_ADDRESS
1264                .saturating_add(GUEST_REGION_SIZE.saturating_mul(5))
1265        );
1266
1267        assert_eq!(
1268            transaction_context
1269                .transaction_frame
1270                .number_of_cpis_in_trace,
1271            3,
1272        );
1273
1274        // Let's go to Instruction #1 (top level)
1275        transaction_context.pop().unwrap();
1276        transaction_context.push().unwrap();
1277        assert_eq!(
1278            transaction_context
1279                .transaction_frame
1280                .current_executing_instruction,
1281            1,
1282        );
1283        assert_eq!(
1284            transaction_context
1285                .transaction_frame
1286                .number_of_cpis_in_trace,
1287            3
1288        );
1289
1290        // Instruction #1 will do a CPI.
1291        transaction_context
1292            .configure_next_cpi_for_tests(
1293                0,
1294                vec![InstructionAccount::new(2, false, true)],
1295                Vec::new(),
1296            )
1297            .unwrap();
1298
1299        transaction_context.push().unwrap();
1300
1301        assert_eq!(
1302            transaction_context
1303                .transaction_frame
1304                .current_executing_instruction,
1305            5,
1306        );
1307
1308        assert_eq!(
1309            transaction_context
1310                .transaction_frame
1311                .total_number_of_instructions_in_trace,
1312            6
1313        );
1314
1315        assert_eq!(
1316            transaction_context
1317                .transaction_frame
1318                .cpi_data_scratchpad
1319                .ptr(),
1320            GUEST_INSTRUCTION_DATA_BASE_ADDRESS.saturating_add(GUEST_REGION_SIZE.saturating_mul(6))
1321        );
1322        assert_eq!(
1323            transaction_context
1324                .transaction_frame
1325                .cpi_accounts_scratchpad
1326                .ptr(),
1327            GUEST_INSTRUCTION_ACCOUNT_BASE_ADDRESS
1328                .saturating_add(GUEST_REGION_SIZE.saturating_mul(6))
1329        );
1330
1331        assert_eq!(
1332            transaction_context
1333                .transaction_frame
1334                .number_of_cpis_in_trace,
1335            4
1336        );
1337        assert_eq!(
1338            transaction_context.number_of_called_instructions_in_trace(),
1339            6
1340        );
1341
1342        // Return from CPI
1343        transaction_context.pop().unwrap();
1344        assert_eq!(
1345            transaction_context
1346                .transaction_frame
1347                .number_of_cpis_in_trace,
1348            4
1349        );
1350        assert_eq!(
1351            transaction_context
1352                .transaction_frame
1353                .current_executing_instruction,
1354            1,
1355        );
1356
1357        transaction_context.pop().unwrap();
1358    }
1359
1360    #[test]
1361    fn test_get_current_instruction_index() {
1362        let transaction_accounts = vec![(Pubkey::new_unique(), AccountSharedData::default()); 3];
1363        let mut transaction_context =
1364            TransactionContext::new(transaction_accounts, Rent::default(), 20, 20, 2);
1365
1366        // First top level instruction
1367        transaction_context
1368            .configure_instruction_at_index(
1369                0,
1370                1,
1371                vec![
1372                    InstructionAccount::new(0, false, false),
1373                    InstructionAccount::new(1, false, false),
1374                ],
1375                vec![u16::MAX; 3],
1376                Cow::Owned(Vec::new()),
1377                None,
1378            )
1379            .unwrap();
1380
1381        // Second top-level instruction
1382        transaction_context
1383            .configure_instruction_at_index(
1384                1,
1385                1,
1386                vec![
1387                    InstructionAccount::new(0, false, false),
1388                    InstructionAccount::new(1, false, true),
1389                ],
1390                vec![u16::MAX; 3],
1391                Cow::Owned(Vec::new()),
1392                None,
1393            )
1394            .unwrap();
1395
1396        transaction_context.push().unwrap();
1397        assert_eq!(
1398            transaction_context.get_current_instruction_index().unwrap(),
1399            0
1400        );
1401
1402        transaction_context.pop().unwrap();
1403
1404        transaction_context.push().unwrap();
1405        assert_eq!(
1406            transaction_context.get_current_instruction_index().unwrap(),
1407            1
1408        );
1409
1410        // Simulating a CPI
1411        transaction_context
1412            .configure_next_cpi_for_tests(
1413                1,
1414                vec![
1415                    InstructionAccount::new(0, false, true),
1416                    InstructionAccount::new(1, false, false),
1417                ],
1418                Vec::new(),
1419            )
1420            .unwrap();
1421        transaction_context.push().unwrap();
1422        assert_eq!(
1423            transaction_context.get_current_instruction_index().unwrap(),
1424            2
1425        );
1426
1427        // Yet another CPI
1428        transaction_context
1429            .configure_next_cpi_for_tests(
1430                1,
1431                vec![
1432                    InstructionAccount::new(0, false, true),
1433                    InstructionAccount::new(1, false, false),
1434                ],
1435                Vec::new(),
1436            )
1437            .unwrap();
1438        transaction_context.push().unwrap();
1439        assert_eq!(
1440            transaction_context.get_current_instruction_index().unwrap(),
1441            3
1442        );
1443
1444        // CPI return
1445        transaction_context.pop().unwrap();
1446        assert_eq!(
1447            transaction_context.get_current_instruction_index().unwrap(),
1448            2
1449        );
1450
1451        // CPI return 2
1452        transaction_context.pop().unwrap();
1453        assert_eq!(
1454            transaction_context.get_current_instruction_index().unwrap(),
1455            1
1456        );
1457    }
1458}