1#![allow(clippy::integer_arithmetic)]
2use crate::sanitize::Sanitize;
5use crate::{pubkey::Pubkey, short_vec};
6use bincode::serialize;
7use borsh::BorshSerialize;
8use serde::Serialize;
9use thiserror::Error;
10
11#[derive(
21 Serialize, Deserialize, Debug, Error, PartialEq, Eq, Clone, AbiExample, AbiEnumVisitor,
22)]
23pub enum InstructionError {
24 #[error("generic instruction error")]
27 GenericError,
28
29 #[error("invalid program argument")]
31 InvalidArgument,
32
33 #[error("invalid instruction data")]
35 InvalidInstructionData,
36
37 #[error("invalid account data for instruction")]
39 InvalidAccountData,
40
41 #[error("account data too small for instruction")]
43 AccountDataTooSmall,
44
45 #[error("insufficient funds for instruction")]
47 InsufficientFunds,
48
49 #[error("incorrect program id for instruction")]
51 IncorrectProgramId,
52
53 #[error("missing required signature for instruction")]
55 MissingRequiredSignature,
56
57 #[error("instruction requires an uninitialized account")]
59 AccountAlreadyInitialized,
60
61 #[error("instruction requires an initialized account")]
63 UninitializedAccount,
64
65 #[error("sum of account balances before and after instruction do not match")]
67 UnbalancedInstruction,
68
69 #[error("instruction modified the program id of an account")]
71 ModifiedProgramId,
72
73 #[error("instruction spent from the balance of an account it does not own")]
75 ExternalAccountCaratSpend,
76
77 #[error("instruction modified data of an account it does not own")]
79 ExternalAccountDataModified,
80
81 #[error("instruction changed the balance of a read-only account")]
83 ReadonlyCaratChange,
84
85 #[error("instruction modified data of a read-only account")]
87 ReadonlyDataModified,
88
89 #[error("instruction contains duplicate accounts")]
92 DuplicateAccountIndex,
93
94 #[error("instruction changed executable bit of an account")]
96 ExecutableModified,
97
98 #[error("instruction modified rent epoch of an account")]
100 RentEpochModified,
101
102 #[error("insufficient account keys for instruction")]
104 NotEnoughAccountKeys,
105
106 #[error("non-system instruction changed account size")]
108 AccountDataSizeChanged,
109
110 #[error("instruction expected an executable account")]
112 AccountNotExecutable,
113
114 #[error("instruction tries to borrow reference for an account which is already borrowed")]
116 AccountBorrowFailed,
117
118 #[error("instruction left account with an outstanding borrowed reference")]
120 AccountBorrowOutstanding,
121
122 #[error("instruction modifications of multiply-passed account differ")]
126 DuplicateAccountOutOfSync,
127
128 #[error("custom program error: {0:#x}")]
132 Custom(u32),
133
134 #[error("program returned invalid error code")]
137 InvalidError,
138
139 #[error("instruction changed executable accounts data")]
141 ExecutableDataModified,
142
143 #[error("instruction changed the balance of a executable account")]
145 ExecutableCaratChange,
146
147 #[error("executable accounts must be rent exempt")]
149 ExecutableAccountNotRentExempt,
150
151 #[error("Unsupported program id")]
153 UnsupportedProgramId,
154
155 #[error("Cross-program invocation call depth too deep")]
157 CallDepth,
158
159 #[error("An account required by the instruction is missing")]
161 MissingAccount,
162
163 #[error("Cross-program invocation reentrancy not allowed for this instruction")]
165 ReentrancyNotAllowed,
166
167 #[error("Length of the seed is too long for address generation")]
169 MaxSeedLengthExceeded,
170
171 #[error("Provided seeds do not result in a valid address")]
173 InvalidSeeds,
174
175 #[error("Failed to reallocate account data")]
177 InvalidRealloc,
178
179 #[error("Computational budget exceeded")]
181 ComputationalBudgetExceeded,
182
183 #[error("Cross-program invocation with unauthorized signer or writable account")]
185 PrivilegeEscalation,
186
187 #[error("Failed to create program execution environment")]
189 ProgramEnvironmentSetupFailure,
190
191 #[error("Program failed to complete")]
193 ProgramFailedToComplete,
194
195 #[error("Program failed to compile")]
197 ProgramFailedToCompile,
198
199 #[error("Account is immutable")]
201 Immutable,
202
203 #[error("Incorrect authority provided")]
205 IncorrectAuthority,
206
207 #[error("Failed to serialize or deserialize account data: {0}")]
217 BorshIoError(String),
218
219 #[error("An account does not have enough carats to be rent-exempt")]
221 AccountNotRentExempt,
222
223 #[error("Invalid account owner")]
225 InvalidAccountOwner,
226
227 #[error("Program arithmetic overflowed")]
229 ArithmeticOverflow,
230
231 #[error("Unsupported sysvar")]
233 UnsupportedSysvar,
234
235 #[error("Provided owner is not allowed")]
237 IllegalOwner,
238 }
241
242#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
243pub struct Instruction {
244 pub program_id: Pubkey,
246 pub accounts: Vec<AccountMeta>,
248 pub data: Vec<u8>,
250}
251
252impl Instruction {
253 #[deprecated(
254 since = "1.6.0",
255 note = "Please use another Instruction constructor instead, such as `Instruction::new_with_bincode`"
256 )]
257 pub fn new<T: Serialize>(program_id: Pubkey, data: &T, accounts: Vec<AccountMeta>) -> Self {
258 Self::new_with_bincode(program_id, data, accounts)
259 }
260
261 pub fn new_with_bincode<T: Serialize>(
262 program_id: Pubkey,
263 data: &T,
264 accounts: Vec<AccountMeta>,
265 ) -> Self {
266 let data = serialize(data).unwrap();
267 Self {
268 program_id,
269 accounts,
270 data,
271 }
272 }
273
274 pub fn new_with_borsh<T: BorshSerialize>(
275 program_id: Pubkey,
276 data: &T,
277 accounts: Vec<AccountMeta>,
278 ) -> Self {
279 let data = data.try_to_vec().unwrap();
280 Self {
281 program_id,
282 accounts,
283 data,
284 }
285 }
286
287 pub fn new_with_bytes(program_id: Pubkey, data: &[u8], accounts: Vec<AccountMeta>) -> Self {
288 Self {
289 program_id,
290 accounts,
291 data: data.to_vec(),
292 }
293 }
294}
295
296pub fn checked_add(a: u64, b: u64) -> Result<u64, InstructionError> {
297 a.checked_add(b).ok_or(InstructionError::InsufficientFunds)
298}
299
300#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
302pub struct AccountMeta {
303 pub pubkey: Pubkey,
305 pub is_signer: bool,
307 pub is_writable: bool,
309}
310
311impl AccountMeta {
312 pub fn new(pubkey: Pubkey, is_signer: bool) -> Self {
313 Self {
314 pubkey,
315 is_signer,
316 is_writable: true,
317 }
318 }
319
320 pub fn new_readonly(pubkey: Pubkey, is_signer: bool) -> Self {
321 Self {
322 pubkey,
323 is_signer,
324 is_writable: false,
325 }
326 }
327}
328
329#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, AbiExample)]
331#[serde(rename_all = "camelCase")]
332pub struct CompiledInstruction {
333 pub program_id_index: u8,
335 #[serde(with = "short_vec")]
337 pub accounts: Vec<u8>,
338 #[serde(with = "short_vec")]
340 pub data: Vec<u8>,
341}
342
343impl Sanitize for CompiledInstruction {}
344
345impl CompiledInstruction {
346 pub fn new<T: Serialize>(program_ids_index: u8, data: &T, accounts: Vec<u8>) -> Self {
347 let data = serialize(data).unwrap();
348 Self {
349 program_id_index: program_ids_index,
350 data,
351 accounts,
352 }
353 }
354
355 pub fn program_id<'a>(&self, program_ids: &'a [Pubkey]) -> &'a Pubkey {
356 &program_ids[self.program_id_index as usize]
357 }
358
359 pub fn visit_each_account(
361 &self,
362 work: &mut dyn FnMut(usize, usize) -> Result<(), InstructionError>,
363 ) -> Result<(), InstructionError> {
364 let mut unique_index = 0;
365 'root: for (i, account_index) in self.accounts.iter().enumerate() {
366 for account_index_before in self.accounts[..i].iter() {
369 if account_index_before == account_index {
370 continue 'root; }
372 }
373 work(unique_index, *account_index as usize)?;
374 unique_index += 1;
375 }
376 Ok(())
377 }
378}
379
380#[cfg(test)]
381mod test {
382 use super::*;
383
384 #[test]
385 fn test_visit_each_account() {
386 let do_work = |accounts: &[u8]| -> (usize, usize) {
387 let mut unique_total = 0;
388 let mut account_total = 0;
389 let mut work = |unique_index: usize, account_index: usize| {
390 unique_total += unique_index;
391 account_total += account_index;
392 Ok(())
393 };
394 let instruction = CompiledInstruction::new(0, &[0], accounts.to_vec());
395 instruction.visit_each_account(&mut work).unwrap();
396
397 (unique_total, account_total)
398 };
399
400 assert_eq!((6, 6), do_work(&[0, 1, 2, 3]));
401 assert_eq!((6, 6), do_work(&[0, 1, 1, 2, 3]));
402 assert_eq!((6, 6), do_work(&[0, 1, 2, 3, 3]));
403 assert_eq!((6, 6), do_work(&[0, 0, 1, 1, 2, 2, 3, 3]));
404 assert_eq!((0, 2), do_work(&[2, 2]));
405 }
406}