atlas_instruction/
lib.rs

1//! Types for directing the execution of Atlas programs.
2//!
3//! Every invocation of a Atlas 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 Atlas 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#![cfg_attr(docsrs, feature(doc_auto_cfg))]
14#![allow(clippy::arithmetic_side_effects)]
15#![no_std]
16
17#[cfg(feature = "std")]
18extern crate std;
19use atlas_pubkey::Pubkey;
20#[cfg(feature = "std")]
21use std::vec::Vec;
22pub mod account_meta;
23#[cfg(feature = "std")]
24pub use account_meta::AccountMeta;
25pub use atlas_instruction_error as error;
26#[cfg(any(feature = "syscalls", target_os = "atlas"))]
27pub mod syscalls;
28
29/// A directive for a single invocation of a Atlas program.
30///
31/// An instruction specifies which program it is calling, which accounts it may
32/// read or modify, and additional data that serves as input to the program. One
33/// or more instructions are included in transactions submitted by Atlas
34/// clients. Instructions are also used to describe [cross-program
35/// invocations][cpi].
36///
37/// [cpi]: https://atlaschain.org/docs/core/cpi
38///
39/// During execution, a program will receive a list of account data as one of
40/// its arguments, in the same order as specified during `Instruction`
41/// construction.
42///
43/// While Atlas is agnostic to the format of the instruction data, it has
44/// built-in support for serialization via [`borsh`] and [`bincode`].
45///
46/// [`borsh`]: https://docs.rs/borsh/latest/borsh/
47/// [`bincode`]: https://docs.rs/bincode/latest/bincode/
48///
49/// # Specifying account metadata
50///
51/// When constructing an [`Instruction`], a list of all accounts that may be
52/// read or written during the execution of that instruction must be supplied as
53/// [`AccountMeta`] values.
54///
55/// Any account whose data may be mutated by the program during execution must
56/// be specified as writable. During execution, writing to an account that was
57/// not specified as writable will cause the transaction to fail. Writing to an
58/// account that is not owned by the program will cause the transaction to fail.
59///
60/// Any account whose lamport balance may be mutated by the program during
61/// execution must be specified as writable. During execution, mutating the
62/// lamports of an account that was not specified as writable will cause the
63/// transaction to fail. While _subtracting_ lamports from an account not owned
64/// by the program will cause the transaction to fail, _adding_ lamports to any
65/// account is allowed, as long is it is mutable.
66///
67/// Accounts that are not read or written by the program may still be specified
68/// in an `Instruction`'s account list. These will affect scheduling of program
69/// execution by the runtime, but will otherwise be ignored.
70///
71/// When building a transaction, the Atlas runtime coalesces all accounts used
72/// by all instructions in that transaction, along with accounts and permissions
73/// required by the runtime, into a single account list. Some accounts and
74/// account permissions required by the runtime to process a transaction are
75/// _not_ required to be included in an `Instruction`s account list. These
76/// include:
77///
78/// - The program ID — it is a separate field of `Instruction`
79/// - The transaction's fee-paying account — it is added during [`Message`]
80///   construction. A program may still require the fee payer as part of the
81///   account list if it directly references it.
82///
83/// [`Message`]: https://docs.rs/atlas-program/latest/atlas_program/message/legacy/struct.Message.html
84///
85/// Programs may require signatures from some accounts, in which case they
86/// should be specified as signers during `Instruction` construction. The
87/// program must still validate during execution that the account is a signer.
88#[cfg(feature = "std")]
89#[cfg_attr(
90    feature = "serde",
91    derive(serde_derive::Serialize, serde_derive::Deserialize)
92)]
93#[derive(Debug, PartialEq, Eq, Clone)]
94pub struct Instruction {
95    /// Pubkey of the program that executes this instruction.
96    pub program_id: Pubkey,
97    /// Metadata describing accounts that should be passed to the program.
98    pub accounts: Vec<AccountMeta>,
99    /// Opaque data passed to the program for its own interpretation.
100    pub data: Vec<u8>,
101}
102
103#[cfg(feature = "std")]
104impl Instruction {
105    #[cfg(feature = "borsh")]
106    /// Create a new instruction from a value, encoded with [`borsh`].
107    ///
108    /// [`borsh`]: https://docs.rs/borsh/latest/borsh/
109    ///
110    /// `program_id` is the address of the program that will execute the instruction.
111    /// `accounts` contains a description of all accounts that may be accessed by the program.
112    ///
113    /// Borsh serialization is often preferred over bincode as it has a stable
114    /// [specification] and an [implementation in JavaScript][jsb], neither of
115    /// which are true of bincode.
116    ///
117    /// [specification]: https://borsh.io/
118    /// [jsb]: https://github.com/near/borsh-js
119    ///
120    /// # Examples
121    ///
122    /// ```
123    /// # use atlas_pubkey::Pubkey;
124    /// # use atlas_instruction::{AccountMeta, Instruction};
125    /// # use borsh::{BorshSerialize, BorshDeserialize};
126    /// #
127    /// #[derive(BorshSerialize, BorshDeserialize)]
128    /// # #[borsh(crate = "borsh")]
129    /// pub struct MyInstruction {
130    ///     pub lamports: u64,
131    /// }
132    ///
133    /// pub fn create_instruction(
134    ///     program_id: &Pubkey,
135    ///     from: &Pubkey,
136    ///     to: &Pubkey,
137    ///     lamports: u64,
138    /// ) -> Instruction {
139    ///     let instr = MyInstruction { lamports };
140    ///
141    ///     Instruction::new_with_borsh(
142    ///         *program_id,
143    ///         &instr,
144    ///         vec![
145    ///             AccountMeta::new(*from, true),
146    ///             AccountMeta::new(*to, false),
147    ///         ],
148    ///    )
149    /// }
150    /// ```
151    pub fn new_with_borsh<T: borsh::BorshSerialize>(
152        program_id: Pubkey,
153        data: &T,
154        accounts: Vec<AccountMeta>,
155    ) -> Self {
156        let data = borsh::to_vec(data).unwrap();
157        Self {
158            program_id,
159            accounts,
160            data,
161        }
162    }
163
164    #[cfg(feature = "bincode")]
165    /// Create a new instruction from a value, encoded with [`bincode`].
166    ///
167    /// [`bincode`]: https://docs.rs/bincode/latest/bincode/
168    ///
169    /// `program_id` is the address of the program that will execute the instruction.
170    /// `accounts` contains a description of all accounts that may be accessed by the program.
171    ///
172    /// # Examples
173    ///
174    /// ```
175    /// # use atlas_pubkey::Pubkey;
176    /// # use atlas_instruction::{AccountMeta, Instruction};
177    /// # use serde::{Serialize, Deserialize};
178    /// #
179    /// #[derive(Serialize, Deserialize)]
180    /// pub struct MyInstruction {
181    ///     pub lamports: u64,
182    /// }
183    ///
184    /// pub fn create_instruction(
185    ///     program_id: &Pubkey,
186    ///     from: &Pubkey,
187    ///     to: &Pubkey,
188    ///     lamports: u64,
189    /// ) -> Instruction {
190    ///     let instr = MyInstruction { lamports };
191    ///
192    ///     Instruction::new_with_bincode(
193    ///         *program_id,
194    ///         &instr,
195    ///         vec![
196    ///             AccountMeta::new(*from, true),
197    ///             AccountMeta::new(*to, false),
198    ///         ],
199    ///    )
200    /// }
201    /// ```
202    pub fn new_with_bincode<T: serde::Serialize>(
203        program_id: Pubkey,
204        data: &T,
205        accounts: Vec<AccountMeta>,
206    ) -> Self {
207        let data = bincode::serialize(data).unwrap();
208        Self {
209            program_id,
210            accounts,
211            data,
212        }
213    }
214
215    /// Create a new instruction from a byte slice.
216    ///
217    /// `program_id` is the address of the program that will execute the instruction.
218    /// `accounts` contains a description of all accounts that may be accessed by the program.
219    ///
220    /// The caller is responsible for ensuring the correct encoding of `data` as expected
221    /// by the callee program.
222    ///
223    /// # Examples
224    ///
225    /// ```
226    /// # use atlas_pubkey::Pubkey;
227    /// # use atlas_instruction::{AccountMeta, Instruction};
228    /// #
229    /// # use borsh::{io::Error, BorshSerialize, BorshDeserialize};
230    /// #
231    /// #[derive(BorshSerialize, BorshDeserialize)]
232    /// # #[borsh(crate = "borsh")]
233    /// pub struct MyInstruction {
234    ///     pub lamports: u64,
235    /// }
236    ///
237    /// pub fn create_instruction(
238    ///     program_id: &Pubkey,
239    ///     from: &Pubkey,
240    ///     to: &Pubkey,
241    ///     lamports: u64,
242    /// ) -> Result<Instruction, Error> {
243    ///     let instr = MyInstruction { lamports };
244    ///
245    ///     let mut instr_in_bytes: Vec<u8> = Vec::new();
246    ///     instr.serialize(&mut instr_in_bytes)?;
247    ///
248    ///     Ok(Instruction::new_with_bytes(
249    ///         *program_id,
250    ///         &instr_in_bytes,
251    ///         vec![
252    ///             AccountMeta::new(*from, true),
253    ///             AccountMeta::new(*to, false),
254    ///         ],
255    ///    ))
256    /// }
257    /// ```
258    pub fn new_with_bytes(program_id: Pubkey, data: &[u8], accounts: Vec<AccountMeta>) -> Self {
259        Self {
260            program_id,
261            accounts,
262            data: data.to_vec(),
263        }
264    }
265}
266
267// Stack height when processing transaction-level instructions
268pub const TRANSACTION_LEVEL_STACK_HEIGHT: usize = 1;
269
270/// Use to query and convey information about the sibling instruction components
271/// when calling the `atlas_get_processed_sibling_instruction` syscall.
272#[repr(C)]
273#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
274pub struct ProcessedSiblingInstruction {
275    /// Length of the instruction data
276    pub data_len: u64,
277    /// Number of AccountMeta structures
278    pub accounts_len: u64,
279}
280
281/// Borrowed version of `AccountMeta`.
282///
283/// This struct is used by the runtime when constructing the instructions sysvar. It is not
284/// useful to Atlas programs.
285pub struct BorrowedAccountMeta<'a> {
286    pub pubkey: &'a Pubkey,
287    pub is_signer: bool,
288    pub is_writable: bool,
289}
290
291/// Borrowed version of `Instruction`.
292///
293/// This struct is used by the runtime when constructing the instructions sysvar. It is not
294/// useful to Atlas programs.
295#[cfg(feature = "std")]
296pub struct BorrowedInstruction<'a> {
297    pub program_id: &'a Pubkey,
298    pub accounts: Vec<BorrowedAccountMeta<'a>>,
299    pub data: &'a [u8],
300}