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}