miraland_program/
instruction.rs

1//! Types for directing the execution of Miraland programs.
2//!
3//! Every invocation of a Miraland program executes a single instruction, as
4//! defined by the [`Instruction`] type. An instruction is primarily a vector of
5//! bytes, the contents of which are program-specific, and not interpreted by
6//! the Miraland runtime. This allows flexibility in how programs behave, how they
7//! are controlled by client software, and what data encodings they use.
8//!
9//! Besides the instruction data, every account a program may read or write
10//! while executing a given instruction is also included in `Instruction`, as
11//! [`AccountMeta`] values. The runtime uses this information to efficiently
12//! schedule execution of transactions.
13
14#![allow(clippy::arithmetic_side_effects)]
15
16use {
17    crate::{pubkey::Pubkey, sanitize::Sanitize, short_vec, wasm_bindgen},
18    bincode::serialize,
19    borsh::BorshSerialize,
20    serde::Serialize,
21    thiserror::Error,
22};
23
24/// Reasons the runtime might have rejected an instruction.
25///
26/// Members of this enum must not be removed, but new ones can be added.
27/// Also, it is crucial that meta-information if any that comes along with
28/// an error be consistent across software versions.  For example, it is
29/// dangerous to include error strings from 3rd party crates because they could
30/// change at any time and changes to them are difficult to detect.
31#[cfg_attr(not(target_os = "solana"), derive(AbiExample, AbiEnumVisitor))]
32#[derive(Serialize, Deserialize, Debug, Error, PartialEq, Eq, Clone)]
33pub enum InstructionError {
34    /// Deprecated! Use CustomError instead!
35    /// The program instruction returned an error
36    #[error("generic instruction error")]
37    GenericError,
38
39    /// The arguments provided to a program were invalid
40    #[error("invalid program argument")]
41    InvalidArgument,
42
43    /// An instruction's data contents were invalid
44    #[error("invalid instruction data")]
45    InvalidInstructionData,
46
47    /// An account's data contents was invalid
48    #[error("invalid account data for instruction")]
49    InvalidAccountData,
50
51    /// An account's data was too small
52    #[error("account data too small for instruction")]
53    AccountDataTooSmall,
54
55    /// An account's balance was too small to complete the instruction
56    #[error("insufficient funds for instruction")]
57    InsufficientFunds,
58
59    /// The account did not have the expected program id
60    #[error("incorrect program id for instruction")]
61    IncorrectProgramId,
62
63    /// A signature was required but not found
64    #[error("missing required signature for instruction")]
65    MissingRequiredSignature,
66
67    /// An initialize instruction was sent to an account that has already been initialized.
68    #[error("instruction requires an uninitialized account")]
69    AccountAlreadyInitialized,
70
71    /// An attempt to operate on an account that hasn't been initialized.
72    #[error("instruction requires an initialized account")]
73    UninitializedAccount,
74
75    /// Program's instruction lamport balance does not equal the balance after the instruction
76    #[error("sum of account balances before and after instruction do not match")]
77    UnbalancedInstruction,
78
79    /// Program illegally modified an account's program id
80    #[error("instruction illegally modified the program id of an account")]
81    ModifiedProgramId,
82
83    /// Program spent the lamports of an account that doesn't belong to it
84    #[error("instruction spent from the balance of an account it does not own")]
85    ExternalAccountLamportSpend,
86
87    /// Program modified the data of an account that doesn't belong to it
88    #[error("instruction modified data of an account it does not own")]
89    ExternalAccountDataModified,
90
91    /// Read-only account's lamports modified
92    #[error("instruction changed the balance of a read-only account")]
93    ReadonlyLamportChange,
94
95    /// Read-only account's data was modified
96    #[error("instruction modified data of a read-only account")]
97    ReadonlyDataModified,
98
99    /// An account was referenced more than once in a single instruction
100    // Deprecated, instructions can now contain duplicate accounts
101    #[error("instruction contains duplicate accounts")]
102    DuplicateAccountIndex,
103
104    /// Executable bit on account changed, but shouldn't have
105    #[error("instruction changed executable bit of an account")]
106    ExecutableModified,
107
108    /// Rent_epoch account changed, but shouldn't have
109    #[error("instruction modified rent epoch of an account")]
110    RentEpochModified,
111
112    /// The instruction expected additional account keys
113    #[error("insufficient account keys for instruction")]
114    NotEnoughAccountKeys,
115
116    /// Program other than the account's owner changed the size of the account data
117    #[error("program other than the account's owner changed the size of the account data")]
118    AccountDataSizeChanged,
119
120    /// The instruction expected an executable account
121    #[error("instruction expected an executable account")]
122    AccountNotExecutable,
123
124    /// Failed to borrow a reference to account data, already borrowed
125    #[error("instruction tries to borrow reference for an account which is already borrowed")]
126    AccountBorrowFailed,
127
128    /// Account data has an outstanding reference after a program's execution
129    #[error("instruction left account with an outstanding borrowed reference")]
130    AccountBorrowOutstanding,
131
132    /// The same account was multiply passed to an on-chain program's entrypoint, but the program
133    /// modified them differently.  A program can only modify one instance of the account because
134    /// the runtime cannot determine which changes to pick or how to merge them if both are modified
135    #[error("instruction modifications of multiply-passed account differ")]
136    DuplicateAccountOutOfSync,
137
138    /// Allows on-chain programs to implement program-specific error types and see them returned
139    /// by the Miraland runtime. A program-specific error may be any type that is represented as
140    /// or serialized to a u32 integer.
141    #[error("custom program error: {0:#x}")]
142    Custom(u32),
143
144    /// The return value from the program was invalid.  Valid errors are either a defined builtin
145    /// error value or a user-defined error in the lower 32 bits.
146    #[error("program returned invalid error code")]
147    InvalidError,
148
149    /// Executable account's data was modified
150    #[error("instruction changed executable accounts data")]
151    ExecutableDataModified,
152
153    /// Executable account's lamports modified
154    #[error("instruction changed the balance of an executable account")]
155    ExecutableLamportChange,
156
157    /// Executable accounts must be rent exempt
158    #[error("executable accounts must be rent exempt")]
159    ExecutableAccountNotRentExempt,
160
161    /// Unsupported program id
162    #[error("Unsupported program id")]
163    UnsupportedProgramId,
164
165    /// Cross-program invocation call depth too deep
166    #[error("Cross-program invocation call depth too deep")]
167    CallDepth,
168
169    /// An account required by the instruction is missing
170    #[error("An account required by the instruction is missing")]
171    MissingAccount,
172
173    /// Cross-program invocation reentrancy not allowed for this instruction
174    #[error("Cross-program invocation reentrancy not allowed for this instruction")]
175    ReentrancyNotAllowed,
176
177    /// Length of the seed is too long for address generation
178    #[error("Length of the seed is too long for address generation")]
179    MaxSeedLengthExceeded,
180
181    /// Provided seeds do not result in a valid address
182    #[error("Provided seeds do not result in a valid address")]
183    InvalidSeeds,
184
185    /// Failed to reallocate account data of this length
186    #[error("Failed to reallocate account data")]
187    InvalidRealloc,
188
189    /// Computational budget exceeded
190    #[error("Computational budget exceeded")]
191    ComputationalBudgetExceeded,
192
193    /// Cross-program invocation with unauthorized signer or writable account
194    #[error("Cross-program invocation with unauthorized signer or writable account")]
195    PrivilegeEscalation,
196
197    /// Failed to create program execution environment
198    #[error("Failed to create program execution environment")]
199    ProgramEnvironmentSetupFailure,
200
201    /// Program failed to complete
202    #[error("Program failed to complete")]
203    ProgramFailedToComplete,
204
205    /// Program failed to compile
206    #[error("Program failed to compile")]
207    ProgramFailedToCompile,
208
209    /// Account is immutable
210    #[error("Account is immutable")]
211    Immutable,
212
213    /// Incorrect authority provided
214    #[error("Incorrect authority provided")]
215    IncorrectAuthority,
216
217    /// Failed to serialize or deserialize account data
218    ///
219    /// Warning: This error should never be emitted by the runtime.
220    ///
221    /// This error includes strings from the underlying 3rd party Borsh crate
222    /// which can be dangerous because the error strings could change across
223    /// Borsh versions. Only programs can use this error because they are
224    /// consistent across Miraland software versions.
225    ///
226    #[error("Failed to serialize or deserialize account data: {0}")]
227    BorshIoError(String),
228
229    /// An account does not have enough lamports to be rent-exempt
230    #[error("An account does not have enough lamports to be rent-exempt")]
231    AccountNotRentExempt,
232
233    /// Invalid account owner
234    #[error("Invalid account owner")]
235    InvalidAccountOwner,
236
237    /// Program arithmetic overflowed
238    #[error("Program arithmetic overflowed")]
239    ArithmeticOverflow,
240
241    /// Unsupported sysvar
242    #[error("Unsupported sysvar")]
243    UnsupportedSysvar,
244
245    /// Illegal account owner
246    #[error("Provided owner is not allowed")]
247    IllegalOwner,
248
249    /// Accounts data allocations exceeded the maximum allowed per transaction
250    #[error("Accounts data allocations exceeded the maximum allowed per transaction")]
251    MaxAccountsDataAllocationsExceeded,
252
253    /// Max accounts exceeded
254    #[error("Max accounts exceeded")]
255    MaxAccountsExceeded,
256
257    /// Max instruction trace length exceeded
258    #[error("Max instruction trace length exceeded")]
259    MaxInstructionTraceLengthExceeded,
260
261    /// Builtin programs must consume compute units
262    #[error("Builtin programs must consume compute units")]
263    BuiltinProgramsMustConsumeComputeUnits,
264    // Note: For any new error added here an equivalent ProgramError and its
265    // conversions must also be added
266}
267
268/// A directive for a single invocation of a Miraland program.
269///
270/// An instruction specifies which program it is calling, which accounts it may
271/// read or modify, and additional data that serves as input to the program. One
272/// or more instructions are included in transactions submitted by Miraland
273/// clients. Instructions are also used to describe [cross-program
274/// invocations][cpi].
275///
276/// [cpi]: https://docs.solana.com/developing/programming-model/calling-between-programs
277///
278/// During execution, a program will receive a list of account data as one of
279/// its arguments, in the same order as specified during `Instruction`
280/// construction.
281///
282/// While Miraland is agnostic to the format of the instruction data, it has
283/// built-in support for serialization via [`borsh`] and [`bincode`].
284///
285/// [`borsh`]: https://docs.rs/borsh/latest/borsh/
286/// [`bincode`]: https://docs.rs/bincode/latest/bincode/
287///
288/// # Specifying account metadata
289///
290/// When constructing an [`Instruction`], a list of all accounts that may be
291/// read or written during the execution of that instruction must be supplied as
292/// [`AccountMeta`] values.
293///
294/// Any account whose data may be mutated by the program during execution must
295/// be specified as writable. During execution, writing to an account that was
296/// not specified as writable will cause the transaction to fail. Writing to an
297/// account that is not owned by the program will cause the transaction to fail.
298///
299/// Any account whose lamport balance may be mutated by the program during
300/// execution must be specified as writable. During execution, mutating the
301/// lamports of an account that was not specified as writable will cause the
302/// transaction to fail. While _subtracting_ lamports from an account not owned
303/// by the program will cause the transaction to fail, _adding_ lamports to any
304/// account is allowed, as long is it is mutable.
305///
306/// Accounts that are not read or written by the program may still be specified
307/// in an `Instruction`'s account list. These will affect scheduling of program
308/// execution by the runtime, but will otherwise be ignored.
309///
310/// When building a transaction, the Miraland runtime coalesces all accounts used
311/// by all instructions in that transaction, along with accounts and permissions
312/// required by the runtime, into a single account list. Some accounts and
313/// account permissions required by the runtime to process a transaction are
314/// _not_ required to be included in an `Instruction`s account list. These
315/// include:
316///
317/// - The program ID — it is a separate field of `Instruction`
318/// - The transaction's fee-paying account — it is added during [`Message`]
319///   construction. A program may still require the fee payer as part of the
320///   account list if it directly references it.
321///
322/// [`Message`]: crate::message::Message
323///
324/// Programs may require signatures from some accounts, in which case they
325/// should be specified as signers during `Instruction` construction. The
326/// program must still validate during execution that the account is a signer.
327#[wasm_bindgen]
328#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
329pub struct Instruction {
330    /// Pubkey of the program that executes this instruction.
331    #[wasm_bindgen(skip)]
332    pub program_id: Pubkey,
333    /// Metadata describing accounts that should be passed to the program.
334    #[wasm_bindgen(skip)]
335    pub accounts: Vec<AccountMeta>,
336    /// Opaque data passed to the program for its own interpretation.
337    #[wasm_bindgen(skip)]
338    pub data: Vec<u8>,
339}
340
341impl Instruction {
342    /// Create a new instruction from a value, encoded with [`borsh`].
343    ///
344    /// [`borsh`]: https://docs.rs/borsh/latest/borsh/
345    ///
346    /// `program_id` is the address of the program that will execute the instruction.
347    /// `accounts` contains a description of all accounts that may be accessed by the program.
348    ///
349    /// Borsh serialization is often preferred over bincode as it has a stable
350    /// [specification] and an [implementation in JavaScript][jsb], neither of
351    /// which are true of bincode.
352    ///
353    /// [specification]: https://borsh.io/
354    /// [jsb]: https://github.com/near/borsh-js
355    ///
356    /// # Examples
357    ///
358    /// ```
359    /// # use miraland_program::{
360    /// #     pubkey::Pubkey,
361    /// #     instruction::{AccountMeta, Instruction},
362    /// # };
363    /// # use borsh::{BorshSerialize, BorshDeserialize};
364    /// #
365    /// #[derive(BorshSerialize, BorshDeserialize)]
366    /// # #[borsh(crate = "borsh")]
367    /// pub struct MyInstruction {
368    ///     pub lamports: u64,
369    /// }
370    ///
371    /// pub fn create_instruction(
372    ///     program_id: &Pubkey,
373    ///     from: &Pubkey,
374    ///     to: &Pubkey,
375    ///     lamports: u64,
376    /// ) -> Instruction {
377    ///     let instr = MyInstruction { lamports };
378    ///
379    ///     Instruction::new_with_borsh(
380    ///         *program_id,
381    ///         &instr,
382    ///         vec![
383    ///             AccountMeta::new(*from, true),
384    ///             AccountMeta::new(*to, false),
385    ///         ],
386    ///    )
387    /// }
388    /// ```
389    pub fn new_with_borsh<T: BorshSerialize>(
390        program_id: Pubkey,
391        data: &T,
392        accounts: Vec<AccountMeta>,
393    ) -> Self {
394        let data = borsh::to_vec(data).unwrap();
395        Self {
396            program_id,
397            accounts,
398            data,
399        }
400    }
401
402    /// Create a new instruction from a value, encoded with [`bincode`].
403    ///
404    /// [`bincode`]: https://docs.rs/bincode/latest/bincode/
405    ///
406    /// `program_id` is the address of the program that will execute the instruction.
407    /// `accounts` contains a description of all accounts that may be accessed by the program.
408    ///
409    /// # Examples
410    ///
411    /// ```
412    /// # use miraland_program::{
413    /// #     pubkey::Pubkey,
414    /// #     instruction::{AccountMeta, Instruction},
415    /// # };
416    /// # use serde::{Serialize, Deserialize};
417    /// #
418    /// #[derive(Serialize, Deserialize)]
419    /// pub struct MyInstruction {
420    ///     pub lamports: u64,
421    /// }
422    ///
423    /// pub fn create_instruction(
424    ///     program_id: &Pubkey,
425    ///     from: &Pubkey,
426    ///     to: &Pubkey,
427    ///     lamports: u64,
428    /// ) -> Instruction {
429    ///     let instr = MyInstruction { lamports };
430    ///
431    ///     Instruction::new_with_bincode(
432    ///         *program_id,
433    ///         &instr,
434    ///         vec![
435    ///             AccountMeta::new(*from, true),
436    ///             AccountMeta::new(*to, false),
437    ///         ],
438    ///    )
439    /// }
440    /// ```
441    pub fn new_with_bincode<T: Serialize>(
442        program_id: Pubkey,
443        data: &T,
444        accounts: Vec<AccountMeta>,
445    ) -> Self {
446        let data = serialize(data).unwrap();
447        Self {
448            program_id,
449            accounts,
450            data,
451        }
452    }
453
454    /// Create a new instruction from a byte slice.
455    ///
456    /// `program_id` is the address of the program that will execute the instruction.
457    /// `accounts` contains a description of all accounts that may be accessed by the program.
458    ///
459    /// The caller is responsible for ensuring the correct encoding of `data` as expected
460    /// by the callee program.
461    ///
462    /// # Examples
463    ///
464    /// ```
465    /// # use miraland_program::{
466    /// #     pubkey::Pubkey,
467    /// #     instruction::{AccountMeta, Instruction},
468    /// # };
469    /// # use borsh::{io::Error, BorshSerialize, BorshDeserialize};
470    /// #
471    /// #[derive(BorshSerialize, BorshDeserialize)]
472    /// # #[borsh(crate = "borsh")]
473    /// pub struct MyInstruction {
474    ///     pub lamports: u64,
475    /// }
476    ///
477    /// pub fn create_instruction(
478    ///     program_id: &Pubkey,
479    ///     from: &Pubkey,
480    ///     to: &Pubkey,
481    ///     lamports: u64,
482    /// ) -> Result<Instruction, Error> {
483    ///     let instr = MyInstruction { lamports };
484    ///
485    ///     let mut instr_in_bytes: Vec<u8> = Vec::new();
486    ///     instr.serialize(&mut instr_in_bytes)?;
487    ///
488    ///     Ok(Instruction::new_with_bytes(
489    ///         *program_id,
490    ///         &instr_in_bytes,
491    ///         vec![
492    ///             AccountMeta::new(*from, true),
493    ///             AccountMeta::new(*to, false),
494    ///         ],
495    ///    ))
496    /// }
497    /// ```
498    pub fn new_with_bytes(program_id: Pubkey, data: &[u8], accounts: Vec<AccountMeta>) -> Self {
499        Self {
500            program_id,
501            accounts,
502            data: data.to_vec(),
503        }
504    }
505
506    #[deprecated(
507        since = "1.6.0",
508        note = "Please use another Instruction constructor instead, such as `Instruction::new_with_borsh`"
509    )]
510    pub fn new<T: Serialize>(program_id: Pubkey, data: &T, accounts: Vec<AccountMeta>) -> Self {
511        Self::new_with_bincode(program_id, data, accounts)
512    }
513}
514
515/// Addition that returns [`InstructionError::InsufficientFunds`] on overflow.
516///
517/// This is an internal utility function.
518#[doc(hidden)]
519pub fn checked_add(a: u64, b: u64) -> Result<u64, InstructionError> {
520    a.checked_add(b).ok_or(InstructionError::InsufficientFunds)
521}
522
523/// Describes a single account read or written by a program during instruction
524/// execution.
525///
526/// When constructing an [`Instruction`], a list of all accounts that may be
527/// read or written during the execution of that instruction must be supplied.
528/// Any account that may be mutated by the program during execution, either its
529/// data or metadata such as held lamports, must be writable.
530///
531/// Note that because the Miraland runtime schedules parallel transaction
532/// execution around which accounts are writable, care should be taken that only
533/// accounts which actually may be mutated are specified as writable. As the
534/// default [`AccountMeta::new`] constructor creates writable accounts, this is
535/// a minor hazard: use [`AccountMeta::new_readonly`] to specify that an account
536/// is not writable.
537#[repr(C)]
538#[derive(Debug, Default, PartialEq, Eq, Clone, Serialize, Deserialize)]
539pub struct AccountMeta {
540    /// An account's public key.
541    pub pubkey: Pubkey,
542    /// True if an `Instruction` requires a `Transaction` signature matching `pubkey`.
543    pub is_signer: bool,
544    /// True if the account data or metadata may be mutated during program execution.
545    pub is_writable: bool,
546}
547
548impl AccountMeta {
549    /// Construct metadata for a writable account.
550    ///
551    /// # Examples
552    ///
553    /// ```
554    /// # use miraland_program::{
555    /// #     pubkey::Pubkey,
556    /// #     instruction::{AccountMeta, Instruction},
557    /// # };
558    /// # use borsh::{BorshSerialize, BorshDeserialize};
559    /// #
560    /// # #[derive(BorshSerialize, BorshDeserialize)]
561    /// # #[borsh(crate = "borsh")]
562    /// # pub struct MyInstruction;
563    /// #
564    /// # let instruction = MyInstruction;
565    /// # let from = Pubkey::new_unique();
566    /// # let to = Pubkey::new_unique();
567    /// # let program_id = Pubkey::new_unique();
568    /// let instr = Instruction::new_with_borsh(
569    ///     program_id,
570    ///     &instruction,
571    ///     vec![
572    ///         AccountMeta::new(from, true),
573    ///         AccountMeta::new(to, false),
574    ///     ],
575    /// );
576    /// ```
577    pub fn new(pubkey: Pubkey, is_signer: bool) -> Self {
578        Self {
579            pubkey,
580            is_signer,
581            is_writable: true,
582        }
583    }
584
585    /// Construct metadata for a read-only account.
586    ///
587    /// # Examples
588    ///
589    /// ```
590    /// # use miraland_program::{
591    /// #     pubkey::Pubkey,
592    /// #     instruction::{AccountMeta, Instruction},
593    /// # };
594    /// # use borsh::{BorshSerialize, BorshDeserialize};
595    /// #
596    /// # #[derive(BorshSerialize, BorshDeserialize)]
597    /// # #[borsh(crate = "borsh")]
598    /// # pub struct MyInstruction;
599    /// #
600    /// # let instruction = MyInstruction;
601    /// # let from = Pubkey::new_unique();
602    /// # let to = Pubkey::new_unique();
603    /// # let from_account_storage = Pubkey::new_unique();
604    /// # let program_id = Pubkey::new_unique();
605    /// let instr = Instruction::new_with_borsh(
606    ///     program_id,
607    ///     &instruction,
608    ///     vec![
609    ///         AccountMeta::new(from, true),
610    ///         AccountMeta::new(to, false),
611    ///         AccountMeta::new_readonly(from_account_storage, false),
612    ///     ],
613    /// );
614    /// ```
615    pub fn new_readonly(pubkey: Pubkey, is_signer: bool) -> Self {
616        Self {
617            pubkey,
618            is_signer,
619            is_writable: false,
620        }
621    }
622}
623
624/// A compact encoding of an instruction.
625///
626/// A `CompiledInstruction` is a component of a multi-instruction [`Message`],
627/// which is the core of a Miraland transaction. It is created during the
628/// construction of `Message`. Most users will not interact with it directly.
629///
630/// [`Message`]: crate::message::Message
631#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, AbiExample)]
632#[serde(rename_all = "camelCase")]
633pub struct CompiledInstruction {
634    /// Index into the transaction keys array indicating the program account that executes this instruction.
635    pub program_id_index: u8,
636    /// Ordered indices into the transaction keys array indicating which accounts to pass to the program.
637    #[serde(with = "short_vec")]
638    pub accounts: Vec<u8>,
639    /// The program input data.
640    #[serde(with = "short_vec")]
641    pub data: Vec<u8>,
642}
643
644impl Sanitize for CompiledInstruction {}
645
646impl CompiledInstruction {
647    pub fn new<T: Serialize>(program_ids_index: u8, data: &T, accounts: Vec<u8>) -> Self {
648        let data = serialize(data).unwrap();
649        Self {
650            program_id_index: program_ids_index,
651            accounts,
652            data,
653        }
654    }
655
656    pub fn new_from_raw_parts(program_id_index: u8, data: Vec<u8>, accounts: Vec<u8>) -> Self {
657        Self {
658            program_id_index,
659            accounts,
660            data,
661        }
662    }
663
664    pub fn program_id<'a>(&self, program_ids: &'a [Pubkey]) -> &'a Pubkey {
665        &program_ids[self.program_id_index as usize]
666    }
667}
668
669/// Use to query and convey information about the sibling instruction components
670/// when calling the `sol_get_processed_sibling_instruction` syscall.
671#[repr(C)]
672#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
673pub struct ProcessedSiblingInstruction {
674    /// Length of the instruction data
675    pub data_len: u64,
676    /// Number of AccountMeta structures
677    pub accounts_len: u64,
678}
679
680/// Returns a sibling instruction from the processed sibling instruction list.
681///
682/// The processed sibling instruction list is a reverse-ordered list of
683/// successfully processed sibling instructions. For example, given the call flow:
684///
685/// A
686/// B -> C -> D
687/// B -> E
688/// B -> F
689///
690/// Then B's processed sibling instruction list is: `[A]`
691/// Then F's processed sibling instruction list is: `[E, C]`
692pub fn get_processed_sibling_instruction(index: usize) -> Option<Instruction> {
693    #[cfg(target_os = "solana")]
694    {
695        let mut meta = ProcessedSiblingInstruction::default();
696        let mut program_id = Pubkey::default();
697
698        if 1 == unsafe {
699            crate::syscalls::sol_get_processed_sibling_instruction(
700                index as u64,
701                &mut meta,
702                &mut program_id,
703                &mut u8::default(),
704                &mut AccountMeta::default(),
705            )
706        } {
707            let mut data = Vec::new();
708            let mut accounts = Vec::new();
709            data.resize_with(meta.data_len as usize, u8::default);
710            accounts.resize_with(meta.accounts_len as usize, AccountMeta::default);
711
712            let _ = unsafe {
713                crate::syscalls::sol_get_processed_sibling_instruction(
714                    index as u64,
715                    &mut meta,
716                    &mut program_id,
717                    data.as_mut_ptr(),
718                    accounts.as_mut_ptr(),
719                )
720            };
721
722            Some(Instruction::new_with_bytes(program_id, &data, accounts))
723        } else {
724            None
725        }
726    }
727
728    #[cfg(not(target_os = "solana"))]
729    crate::program_stubs::sol_get_processed_sibling_instruction(index)
730}
731
732// Stack height when processing transaction-level instructions
733pub const TRANSACTION_LEVEL_STACK_HEIGHT: usize = 1;
734
735/// Get the current stack height, transaction-level instructions are height
736/// TRANSACTION_LEVEL_STACK_HEIGHT, fist invoked inner instruction is height
737/// TRANSACTION_LEVEL_STACK_HEIGHT + 1, etc...
738pub fn get_stack_height() -> usize {
739    #[cfg(target_os = "solana")]
740    unsafe {
741        crate::syscalls::sol_get_stack_height() as usize
742    }
743
744    #[cfg(not(target_os = "solana"))]
745    {
746        crate::program_stubs::sol_get_stack_height() as usize
747    }
748}