solana_program/
instruction.rs

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