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}