cbe_program/
instruction.rs

1//! Types for directing the execution of Cartallum CBE programs.
2//!
3//! Every invocation of a Cartallum CBE 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 Cartallum CBE 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 scoobie 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 scoobies of an account that doesn't belong to it
87    #[error("instruction spent from the balance of an account it does not own")]
88    ExternalAccountScoobieSpend,
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 scoobies modified
95    #[error("instruction changed the balance of a read-only account")]
96    ReadonlyScoobieChange,
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 Cartallum CBE 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 scoobies modified
157    #[error("instruction changed the balance of a executable account")]
158    ExecutableScoobieChange,
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 Cartallum CBE software versions.
228    ///
229    #[error("Failed to serialize or deserialize account data: {0}")]
230    BorshIoError(String),
231
232    /// An account does not have enough scoobies to be rent-exempt
233    #[error("An account does not have enough scoobies 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    /// Accounts data allocations exceeded the maximum allowed per transaction
253    #[error("Accounts data allocations exceeded the maximum allowed per transaction")]
254    MaxAccountsDataAllocationsExceeded,
255
256    /// Max accounts exceeded
257    #[error("Max accounts exceeded")]
258    MaxAccountsExceeded,
259
260    /// Max instruction trace length exceeded
261    #[error("Max instruction trace length exceeded")]
262    MaxInstructionTraceLengthExceeded,
263    // Note: For any new error added here an equivalent ProgramError and its
264    // conversions must also be added
265}
266
267/// A directive for a single invocation of a Cartallum CBE program.
268///
269/// An instruction specifies which program it is calling, which accounts it may
270/// read or modify, and additional data that serves as input to the program. One
271/// or more instructions are included in transactions submitted by Cartallum CBE
272/// clients. Instructions are also used to describe [cross-program
273/// invocations][cpi].
274///
275/// [cpi]: https://docs.cartallum.com/developing/programming-model/calling-between-programs
276///
277/// During execution, a program will receive a list of account data as one of
278/// its arguments, in the same order as specified during `Instruction`
279/// construction.
280///
281/// While Cartallum CBE is agnostic to the format of the instruction data, it has
282/// built-in support for serialization via [`borsh`] and [`bincode`].
283///
284/// [`borsh`]: https://docs.cartallum.com/borsh/latest/borsh/
285/// [`bincode`]: https://docs.cartallum.com/bincode/latest/bincode/
286///
287/// # Specifying account metadata
288///
289/// When constructing an [`Instruction`], a list of all accounts that may be
290/// read or written during the execution of that instruction must be supplied as
291/// [`AccountMeta`] values.
292///
293/// Any account whose data may be mutated by the program during execution must
294/// be specified as writable. During execution, writing to an account that was
295/// not specified as writable will cause the transaction to fail. Writing to an
296/// account that is not owned by the program will cause the transaction to fail.
297///
298/// Any account whose scoobie balance may be mutated by the program during
299/// execution must be specified as writable. During execution, mutating the
300/// scoobies of an account that was not specified as writable will cause the
301/// transaction to fail. While _subtracting_ scoobies from an account not owned
302/// by the program will cause the transaction to fail, _adding_ scoobies to any
303/// account is allowed, as long is it is mutable.
304///
305/// Accounts that are not read or written by the program may still be specified
306/// in an `Instruction`'s account list. These will affect scheduling of program
307/// execution by the runtime, but will otherwise be ignored.
308///
309/// When building a transaction, the Cartallum CBE runtime coalesces all accounts used
310/// by all instructions in that transaction, along with accounts and permissions
311/// required by the runtime, into a single account list. Some accounts and
312/// account permissions required by the runtime to process a transaction are
313/// _not_ required to be included in an `Instruction`s account list. These
314/// include:
315///
316/// - The program ID — it is a separate field of `Instruction`
317/// - The transaction's fee-paying account — it is added during [`Message`]
318///   construction. A program may still require the fee payer as part of the
319///   account list if it directly references it.
320///
321/// [`Message`]: crate::message::Message
322///
323/// Programs may require signatures from some accounts, in which case they
324/// should be specified as signers during `Instruction` construction. The
325/// program must still validate during execution that the account is a signer.
326#[wasm_bindgen]
327#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
328pub struct Instruction {
329    /// Pubkey of the program that executes this instruction.
330    #[wasm_bindgen(skip)]
331    pub program_id: Pubkey,
332    /// Metadata describing accounts that should be passed to the program.
333    #[wasm_bindgen(skip)]
334    pub accounts: Vec<AccountMeta>,
335    /// Opaque data passed to the program for its own interpretation.
336    #[wasm_bindgen(skip)]
337    pub data: Vec<u8>,
338}
339
340impl Instruction {
341    /// Create a new instruction from a value, encoded with [`borsh`].
342    ///
343    /// [`borsh`]: https://docs.cartallum.com/borsh/latest/borsh/
344    ///
345    /// `program_id` is the address of the program that will execute the instruction.
346    /// `accounts` contains a description of all accounts that may be accessed by the program.
347    ///
348    /// Borsh serialization is often prefered over bincode as it has a stable
349    /// [specification] and an [implementation in JavaScript][jsb], neither of
350    /// which are true of bincode.
351    ///
352    /// [specification]: https://borsh.io/
353    /// [jsb]: https://github.com/near/borsh-js
354    ///
355    /// # Examples
356    ///
357    /// ```
358    /// # use cbe_program::{
359    /// #     pubkey::Pubkey,
360    /// #     instruction::{AccountMeta, Instruction},
361    /// # };
362    /// # use borsh::{BorshSerialize, BorshDeserialize};
363    /// #
364    /// #[derive(BorshSerialize, BorshDeserialize)]
365    /// pub struct MyInstruction {
366    ///     pub scoobies: u64,
367    /// }
368    ///
369    /// pub fn create_instruction(
370    ///     program_id: &Pubkey,
371    ///     from: &Pubkey,
372    ///     to: &Pubkey,
373    ///     scoobies: u64,
374    /// ) -> Instruction {
375    ///     let instr = MyInstruction { scoobies };
376    ///
377    ///     Instruction::new_with_borsh(
378    ///         *program_id,
379    ///         &instr,
380    ///         vec![
381    ///             AccountMeta::new(*from, true),
382    ///             AccountMeta::new(*to, false),
383    ///         ],
384    ///    )
385    /// }
386    /// ```
387    pub fn new_with_borsh<T: BorshSerialize>(
388        program_id: Pubkey,
389        data: &T,
390        accounts: Vec<AccountMeta>,
391    ) -> Self {
392        let data = data.try_to_vec().unwrap();
393        Self {
394            program_id,
395            accounts,
396            data,
397        }
398    }
399
400    /// Create a new instruction from a value, encoded with [`bincode`].
401    ///
402    /// [`bincode`]: https://docs.cartallum.com/bincode/latest/bincode/
403    ///
404    /// `program_id` is the address of the program that will execute the instruction.
405    /// `accounts` contains a description of all accounts that may be accessed by the program.
406    ///
407    /// # Examples
408    ///
409    /// ```
410    /// # use cbe_program::{
411    /// #     pubkey::Pubkey,
412    /// #     instruction::{AccountMeta, Instruction},
413    /// # };
414    /// # use serde::{Serialize, Deserialize};
415    /// #
416    /// #[derive(Serialize, Deserialize)]
417    /// pub struct MyInstruction {
418    ///     pub scoobies: u64,
419    /// }
420    ///
421    /// pub fn create_instruction(
422    ///     program_id: &Pubkey,
423    ///     from: &Pubkey,
424    ///     to: &Pubkey,
425    ///     scoobies: u64,
426    /// ) -> Instruction {
427    ///     let instr = MyInstruction { scoobies };
428    ///
429    ///     Instruction::new_with_bincode(
430    ///         *program_id,
431    ///         &instr,
432    ///         vec![
433    ///             AccountMeta::new(*from, true),
434    ///             AccountMeta::new(*to, false),
435    ///         ],
436    ///    )
437    /// }
438    /// ```
439    pub fn new_with_bincode<T: Serialize>(
440        program_id: Pubkey,
441        data: &T,
442        accounts: Vec<AccountMeta>,
443    ) -> Self {
444        let data = serialize(data).unwrap();
445        Self {
446            program_id,
447            accounts,
448            data,
449        }
450    }
451
452    /// Create a new instruction from a byte slice.
453    ///
454    /// `program_id` is the address of the program that will execute the instruction.
455    /// `accounts` contains a description of all accounts that may be accessed by the program.
456    ///
457    /// The caller is responsible for ensuring the correct encoding of `data` as expected
458    /// by the callee program.
459    ///
460    /// # Examples
461    ///
462    /// ```
463    /// # use cbe_program::{
464    /// #     pubkey::Pubkey,
465    /// #     instruction::{AccountMeta, Instruction},
466    /// # };
467    /// # use borsh::{BorshSerialize, BorshDeserialize};
468    /// # use anyhow::Result;
469    /// #
470    /// #[derive(BorshSerialize, BorshDeserialize)]
471    /// pub struct MyInstruction {
472    ///     pub scoobies: u64,
473    /// }
474    ///
475    /// pub fn create_instruction(
476    ///     program_id: &Pubkey,
477    ///     from: &Pubkey,
478    ///     to: &Pubkey,
479    ///     scoobies: u64,
480    /// ) -> Result<Instruction> {
481    ///     let instr = MyInstruction { scoobies };
482    ///
483    ///     let mut instr_in_bytes: Vec<u8> = Vec::new();
484    ///     instr.serialize(&mut instr_in_bytes)?;
485    ///
486    ///     Ok(Instruction::new_with_bytes(
487    ///         *program_id,
488    ///         &instr_in_bytes,
489    ///         vec![
490    ///             AccountMeta::new(*from, true),
491    ///             AccountMeta::new(*to, false),
492    ///         ],
493    ///    ))
494    /// }
495    /// ```
496    pub fn new_with_bytes(program_id: Pubkey, data: &[u8], accounts: Vec<AccountMeta>) -> Self {
497        Self {
498            program_id,
499            accounts,
500            data: data.to_vec(),
501        }
502    }
503
504    #[deprecated(
505        since = "1.6.0",
506        note = "Please use another Instruction constructor instead, such as `Instruction::new_with_borsh`"
507    )]
508    pub fn new<T: Serialize>(program_id: Pubkey, data: &T, accounts: Vec<AccountMeta>) -> Self {
509        Self::new_with_bincode(program_id, data, accounts)
510    }
511}
512
513/// Addition that returns [`InstructionError::InsufficientFunds`] on overflow.
514///
515/// This is an internal utility function.
516#[doc(hidden)]
517pub fn checked_add(a: u64, b: u64) -> Result<u64, InstructionError> {
518    a.checked_add(b).ok_or(InstructionError::InsufficientFunds)
519}
520
521/// Describes a single account read or written by a program during instruction
522/// execution.
523///
524/// When constructing an [`Instruction`], a list of all accounts that may be
525/// read or written during the execution of that instruction must be supplied.
526/// Any account that may be mutated by the program during execution, either its
527/// data or metadata such as held scoobies, must be writable.
528///
529/// Note that because the Cartallum CBE runtime schedules parallel transaction
530/// execution around which accounts are writable, care should be taken that only
531/// accounts which actually may be mutated are specified as writable. As the
532/// default [`AccountMeta::new`] constructor creates writable accounts, this is
533/// a minor hazard: use [`AccountMeta::new_readonly`] to specify that an account
534/// is not writable.
535#[repr(C)]
536#[derive(Debug, Default, PartialEq, Eq, Clone, Serialize, Deserialize)]
537pub struct AccountMeta {
538    /// An account's public key.
539    pub pubkey: Pubkey,
540    /// True if an `Instruction` requires a `Transaction` signature matching `pubkey`.
541    pub is_signer: bool,
542    /// True if the account data or metadata may be mutated during program execution.
543    pub is_writable: bool,
544}
545
546impl AccountMeta {
547    /// Construct metadata for a writable account.
548    ///
549    /// # Examples
550    ///
551    /// ```
552    /// # use cbe_program::{
553    /// #     pubkey::Pubkey,
554    /// #     instruction::{AccountMeta, Instruction},
555    /// # };
556    /// # use borsh::{BorshSerialize, BorshDeserialize};
557    /// #
558    /// # #[derive(BorshSerialize, BorshDeserialize)]
559    /// # pub struct MyInstruction;
560    /// #
561    /// # let instruction = MyInstruction;
562    /// # let from = Pubkey::new_unique();
563    /// # let to = Pubkey::new_unique();
564    /// # let program_id = Pubkey::new_unique();
565    /// let instr = Instruction::new_with_borsh(
566    ///     program_id,
567    ///     &instruction,
568    ///     vec![
569    ///         AccountMeta::new(from, true),
570    ///         AccountMeta::new(to, false),
571    ///     ],
572    /// );
573    /// ```
574    pub fn new(pubkey: Pubkey, is_signer: bool) -> Self {
575        Self {
576            pubkey,
577            is_signer,
578            is_writable: true,
579        }
580    }
581
582    /// Construct metadata for a read-only account.
583    ///
584    /// # Examples
585    ///
586    /// ```
587    /// # use cbe_program::{
588    /// #     pubkey::Pubkey,
589    /// #     instruction::{AccountMeta, Instruction},
590    /// # };
591    /// # use borsh::{BorshSerialize, BorshDeserialize};
592    /// #
593    /// # #[derive(BorshSerialize, BorshDeserialize)]
594    /// # pub struct MyInstruction;
595    /// #
596    /// # let instruction = MyInstruction;
597    /// # let from = Pubkey::new_unique();
598    /// # let to = Pubkey::new_unique();
599    /// # let from_account_storage = Pubkey::new_unique();
600    /// # let program_id = Pubkey::new_unique();
601    /// let instr = Instruction::new_with_borsh(
602    ///     program_id,
603    ///     &instruction,
604    ///     vec![
605    ///         AccountMeta::new(from, true),
606    ///         AccountMeta::new(to, false),
607    ///         AccountMeta::new_readonly(from_account_storage, false),
608    ///     ],
609    /// );
610    /// ```
611    pub fn new_readonly(pubkey: Pubkey, is_signer: bool) -> Self {
612        Self {
613            pubkey,
614            is_signer,
615            is_writable: false,
616        }
617    }
618}
619
620/// A compact encoding of an instruction.
621///
622/// A `CompiledInstruction` is a component of a multi-instruction [`Message`],
623/// which is the core of a Cartallum CBE transaction. It is created during the
624/// construction of `Message`. Most users will not interact with it directly.
625///
626/// [`Message`]: crate::message::Message
627#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, AbiExample)]
628#[serde(rename_all = "camelCase")]
629pub struct CompiledInstruction {
630    /// Index into the transaction keys array indicating the program account that executes this instruction.
631    pub program_id_index: u8,
632    /// Ordered indices into the transaction keys array indicating which accounts to pass to the program.
633    #[serde(with = "short_vec")]
634    pub accounts: Vec<u8>,
635    /// The program input data.
636    #[serde(with = "short_vec")]
637    pub data: Vec<u8>,
638}
639
640impl Sanitize for CompiledInstruction {}
641
642impl CompiledInstruction {
643    pub fn new<T: Serialize>(program_ids_index: u8, data: &T, accounts: Vec<u8>) -> Self {
644        let data = serialize(data).unwrap();
645        Self {
646            program_id_index: program_ids_index,
647            accounts,
648            data,
649        }
650    }
651
652    pub fn new_from_raw_parts(program_id_index: u8, data: Vec<u8>, accounts: Vec<u8>) -> Self {
653        Self {
654            program_id_index,
655            accounts,
656            data,
657        }
658    }
659
660    pub fn program_id<'a>(&self, program_ids: &'a [Pubkey]) -> &'a Pubkey {
661        &program_ids[self.program_id_index as usize]
662    }
663}
664
665/// Use to query and convey information about the sibling instruction components
666/// when calling the `cbe_get_processed_sibling_instruction` syscall.
667#[repr(C)]
668#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
669pub struct ProcessedSiblingInstruction {
670    /// Length of the instruction data
671    pub data_len: u64,
672    /// Number of AccountMeta structures
673    pub accounts_len: u64,
674}
675
676/// Returns a sibling instruction from the processed sibling instruction list.
677///
678/// The processed sibling instruction list is a reverse-ordered list of
679/// successfully processed sibling instructions. For example, given the call flow:
680///
681/// A
682/// B -> C -> D
683/// B -> E
684/// B -> F
685///
686/// Then B's processed sibling instruction list is: `[A]`
687/// Then F's processed sibling instruction list is: `[E, C]`
688pub fn get_processed_sibling_instruction(index: usize) -> Option<Instruction> {
689    #[cfg(target_os = "cbe")]
690    {
691        let mut meta = ProcessedSiblingInstruction::default();
692        let mut program_id = Pubkey::default();
693
694        if 1 == unsafe {
695            crate::syscalls::cbe_get_processed_sibling_instruction(
696                index as u64,
697                &mut meta,
698                &mut program_id,
699                &mut u8::default(),
700                &mut AccountMeta::default(),
701            )
702        } {
703            let mut data = Vec::new();
704            let mut accounts = Vec::new();
705            data.resize_with(meta.data_len as usize, u8::default);
706            accounts.resize_with(meta.accounts_len as usize, AccountMeta::default);
707
708            let _ = unsafe {
709                crate::syscalls::cbe_get_processed_sibling_instruction(
710                    index as u64,
711                    &mut meta,
712                    &mut program_id,
713                    data.as_mut_ptr(),
714                    accounts.as_mut_ptr(),
715                )
716            };
717
718            Some(Instruction::new_with_bytes(program_id, &data, accounts))
719        } else {
720            None
721        }
722    }
723
724    #[cfg(not(target_os = "cbe"))]
725    crate::program_stubs::cbe_get_processed_sibling_instruction(index)
726}
727
728// Stack height when processing transaction-level instructions
729pub const TRANSACTION_LEVEL_STACK_HEIGHT: usize = 1;
730
731/// Get the current stack height, transaction-level instructions are height
732/// TRANSACTION_LEVEL_STACK_HEIGHT, fist invoked inner instruction is height
733/// TRANSACTION_LEVEL_STACK_HEIGHT + 1, etc...
734pub fn get_stack_height() -> usize {
735    #[cfg(target_os = "cbe")]
736    unsafe {
737        crate::syscalls::cbe_get_stack_height() as usize
738    }
739
740    #[cfg(not(target_os = "cbe"))]
741    {
742        crate::program_stubs::cbe_get_stack_height() as usize
743    }
744}
745
746#[test]
747fn test_account_meta_layout() {
748    #[derive(Debug, Default, PartialEq, Eq, Clone, Serialize, Deserialize)]
749    struct AccountMetaRust {
750        pub pubkey: Pubkey,
751        pub is_signer: bool,
752        pub is_writable: bool,
753    }
754
755    let account_meta_rust = AccountMetaRust::default();
756    let base_rust_addr = &account_meta_rust as *const _ as u64;
757    let pubkey_rust_addr = &account_meta_rust.pubkey as *const _ as u64;
758    let is_signer_rust_addr = &account_meta_rust.is_signer as *const _ as u64;
759    let is_writable_rust_addr = &account_meta_rust.is_writable as *const _ as u64;
760
761    let account_meta_c = AccountMeta::default();
762    let base_c_addr = &account_meta_c as *const _ as u64;
763    let pubkey_c_addr = &account_meta_c.pubkey as *const _ as u64;
764    let is_signer_c_addr = &account_meta_c.is_signer as *const _ as u64;
765    let is_writable_c_addr = &account_meta_c.is_writable as *const _ as u64;
766
767    assert_eq!(
768        std::mem::size_of::<AccountMetaRust>(),
769        std::mem::size_of::<AccountMeta>()
770    );
771    assert_eq!(
772        pubkey_rust_addr - base_rust_addr,
773        pubkey_c_addr - base_c_addr
774    );
775    assert_eq!(
776        is_signer_rust_addr - base_rust_addr,
777        is_signer_c_addr - base_c_addr
778    );
779    assert_eq!(
780        is_writable_rust_addr - base_rust_addr,
781        is_writable_c_addr - base_c_addr
782    );
783}