solana_instruction_error/
lib.rs

1#![no_std]
2#![cfg_attr(feature = "frozen-abi", feature(min_specialization))]
3#[cfg(feature = "num-traits")]
4use num_traits::ToPrimitive;
5#[cfg(feature = "frozen-abi")]
6use solana_frozen_abi_macro::{AbiEnumVisitor, AbiExample};
7#[cfg(feature = "frozen-abi")]
8extern crate std;
9pub use solana_program_error::{
10    ACCOUNT_ALREADY_INITIALIZED, ACCOUNT_BORROW_FAILED, ACCOUNT_DATA_TOO_SMALL,
11    ACCOUNT_NOT_RENT_EXEMPT, ARITHMETIC_OVERFLOW, BORSH_IO_ERROR,
12    BUILTIN_PROGRAMS_MUST_CONSUME_COMPUTE_UNITS, CUSTOM_ZERO, ILLEGAL_OWNER, IMMUTABLE,
13    INCORRECT_AUTHORITY, INCORRECT_PROGRAM_ID, INSUFFICIENT_FUNDS, INVALID_ACCOUNT_DATA,
14    INVALID_ACCOUNT_DATA_REALLOC, INVALID_ACCOUNT_OWNER, INVALID_ARGUMENT,
15    INVALID_INSTRUCTION_DATA, INVALID_SEEDS, MAX_ACCOUNTS_DATA_ALLOCATIONS_EXCEEDED,
16    MAX_INSTRUCTION_TRACE_LENGTH_EXCEEDED, MAX_SEED_LENGTH_EXCEEDED, MISSING_REQUIRED_SIGNATURES,
17    NOT_ENOUGH_ACCOUNT_KEYS, UNINITIALIZED_ACCOUNT, UNSUPPORTED_SYSVAR,
18};
19use {core::fmt, solana_program_error::ProgramError};
20
21/// Reasons the runtime might have rejected an instruction.
22///
23/// Members of this enum must not be removed, but new ones can be added.
24/// Also, it is crucial that meta-information if any that comes along with
25/// an error be consistent across software versions.  For example, it is
26/// dangerous to include error strings from 3rd party crates because they could
27/// change at any time and changes to them are difficult to detect.
28#[cfg_attr(feature = "frozen-abi", derive(AbiExample, AbiEnumVisitor))]
29#[cfg_attr(
30    feature = "serde",
31    derive(serde_derive::Serialize, serde_derive::Deserialize)
32)]
33#[derive(Debug, PartialEq, Eq, Clone)]
34pub enum InstructionError {
35    /// Deprecated! Use CustomError instead!
36    /// The program instruction returned an error
37    GenericError,
38
39    /// The arguments provided to a program were invalid
40    InvalidArgument,
41
42    /// An instruction's data contents were invalid
43    InvalidInstructionData,
44
45    /// An account's data contents was invalid
46    InvalidAccountData,
47
48    /// An account's data was too small
49    AccountDataTooSmall,
50
51    /// An account's balance was too small to complete the instruction
52    InsufficientFunds,
53
54    /// The account did not have the expected program id
55    IncorrectProgramId,
56
57    /// A signature was required but not found
58    MissingRequiredSignature,
59
60    /// An initialize instruction was sent to an account that has already been initialized.
61    AccountAlreadyInitialized,
62
63    /// An attempt to operate on an account that hasn't been initialized.
64    UninitializedAccount,
65
66    /// Program's instruction lamport balance does not equal the balance after the instruction
67    UnbalancedInstruction,
68
69    /// Program illegally modified an account's program id
70    ModifiedProgramId,
71
72    /// Program spent the lamports of an account that doesn't belong to it
73    ExternalAccountLamportSpend,
74
75    /// Program modified the data of an account that doesn't belong to it
76    ExternalAccountDataModified,
77
78    /// Read-only account's lamports modified
79    ReadonlyLamportChange,
80
81    /// Read-only account's data was modified
82    ReadonlyDataModified,
83
84    /// An account was referenced more than once in a single instruction
85    // Deprecated, instructions can now contain duplicate accounts
86    DuplicateAccountIndex,
87
88    /// Executable bit on account changed, but shouldn't have
89    ExecutableModified,
90
91    /// Rent_epoch account changed, but shouldn't have
92    RentEpochModified,
93
94    /// The instruction expected additional account keys
95    NotEnoughAccountKeys,
96
97    /// Program other than the account's owner changed the size of the account data
98    AccountDataSizeChanged,
99
100    /// The instruction expected an executable account
101    AccountNotExecutable,
102
103    /// Failed to borrow a reference to account data, already borrowed
104    AccountBorrowFailed,
105
106    /// Account data has an outstanding reference after a program's execution
107    AccountBorrowOutstanding,
108
109    /// The same account was multiply passed to an on-chain program's entrypoint, but the program
110    /// modified them differently.  A program can only modify one instance of the account because
111    /// the runtime cannot determine which changes to pick or how to merge them if both are modified
112    DuplicateAccountOutOfSync,
113
114    /// Allows on-chain programs to implement program-specific error types and see them returned
115    /// by the Solana runtime. A program-specific error may be any type that is represented as
116    /// or serialized to a u32 integer.
117    Custom(u32),
118
119    /// The return value from the program was invalid.  Valid errors are either a defined builtin
120    /// error value or a user-defined error in the lower 32 bits.
121    InvalidError,
122
123    /// Executable account's data was modified
124    ExecutableDataModified,
125
126    /// Executable account's lamports modified
127    ExecutableLamportChange,
128
129    /// Executable accounts must be rent exempt
130    ExecutableAccountNotRentExempt,
131
132    /// Unsupported program id
133    UnsupportedProgramId,
134
135    /// Cross-program invocation call depth too deep
136    CallDepth,
137
138    /// An account required by the instruction is missing
139    MissingAccount,
140
141    /// Cross-program invocation reentrancy not allowed for this instruction
142    ReentrancyNotAllowed,
143
144    /// Length of the seed is too long for address generation
145    MaxSeedLengthExceeded,
146
147    /// Provided seeds do not result in a valid address
148    InvalidSeeds,
149
150    /// Failed to reallocate account data of this length
151    InvalidRealloc,
152
153    /// Computational budget exceeded
154    ComputationalBudgetExceeded,
155
156    /// Cross-program invocation with unauthorized signer or writable account
157    PrivilegeEscalation,
158
159    /// Failed to create program execution environment
160    ProgramEnvironmentSetupFailure,
161
162    /// Program failed to complete
163    ProgramFailedToComplete,
164
165    /// Program failed to compile
166    ProgramFailedToCompile,
167
168    /// Account is immutable
169    Immutable,
170
171    /// Incorrect authority provided
172    IncorrectAuthority,
173
174    /// Failed to serialize or deserialize account data
175    BorshIoError,
176
177    /// An account does not have enough lamports to be rent-exempt
178    AccountNotRentExempt,
179
180    /// Invalid account owner
181    InvalidAccountOwner,
182
183    /// Program arithmetic overflowed
184    ArithmeticOverflow,
185
186    /// Unsupported sysvar
187    UnsupportedSysvar,
188
189    /// Illegal account owner
190    IllegalOwner,
191
192    /// Accounts data allocations exceeded the maximum allowed per transaction
193    MaxAccountsDataAllocationsExceeded,
194
195    /// Max accounts exceeded
196    MaxAccountsExceeded,
197
198    /// Max instruction trace length exceeded
199    MaxInstructionTraceLengthExceeded,
200
201    /// Builtin programs must consume compute units
202    BuiltinProgramsMustConsumeComputeUnits,
203    // Note: For any new error added here an equivalent ProgramError and its
204    // conversions must also be added
205}
206
207impl core::error::Error for InstructionError {}
208
209impl fmt::Display for InstructionError {
210    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
211        match self {
212            InstructionError::GenericError => f.write_str("generic instruction error"),
213            InstructionError::InvalidArgument => f.write_str("invalid program argument"),
214            InstructionError::InvalidInstructionData => f.write_str("invalid instruction data"),
215            InstructionError::InvalidAccountData => {
216                f.write_str("invalid account data for instruction")
217            }
218            InstructionError::AccountDataTooSmall => {
219                f.write_str("account data too small for instruction")
220            }
221            InstructionError::InsufficientFunds => {
222                f.write_str("insufficient funds for instruction")
223            }
224            InstructionError::IncorrectProgramId => {
225                f.write_str("incorrect program id for instruction")
226            }
227            InstructionError::MissingRequiredSignature => {
228                f.write_str("missing required signature for instruction")
229            }
230            InstructionError::AccountAlreadyInitialized => {
231                f.write_str("instruction requires an uninitialized account")
232            }
233            InstructionError::UninitializedAccount => {
234                f.write_str("instruction requires an initialized account")
235            }
236            InstructionError::UnbalancedInstruction => {
237                f.write_str("sum of account balances before and after instruction do not match")
238            }
239            InstructionError::ModifiedProgramId => {
240                f.write_str("instruction illegally modified the program id of an account")
241            }
242            InstructionError::ExternalAccountLamportSpend => {
243                f.write_str("instruction spent from the balance of an account it does not own")
244            }
245            InstructionError::ExternalAccountDataModified => {
246                f.write_str("instruction modified data of an account it does not own")
247            }
248            InstructionError::ReadonlyLamportChange => {
249                f.write_str("instruction changed the balance of a read-only account")
250            }
251            InstructionError::ReadonlyDataModified => {
252                f.write_str("instruction modified data of a read-only account")
253            }
254            InstructionError::DuplicateAccountIndex => {
255                f.write_str("instruction contains duplicate accounts")
256            }
257            InstructionError::ExecutableModified => {
258                f.write_str("instruction changed executable bit of an account")
259            }
260            InstructionError::RentEpochModified => {
261                f.write_str("instruction modified rent epoch of an account")
262            }
263            InstructionError::NotEnoughAccountKeys => {
264                f.write_str("insufficient account keys for instruction")
265            }
266            InstructionError::AccountDataSizeChanged => f.write_str(
267                "program other than the account's owner changed the size of the account data",
268            ),
269            InstructionError::AccountNotExecutable => {
270                f.write_str("instruction expected an executable account")
271            }
272            InstructionError::AccountBorrowFailed => f.write_str(
273                "instruction tries to borrow reference for an account which is already borrowed",
274            ),
275            InstructionError::AccountBorrowOutstanding => {
276                f.write_str("instruction left account with an outstanding borrowed reference")
277            }
278            InstructionError::DuplicateAccountOutOfSync => {
279                f.write_str("instruction modifications of multiply-passed account differ")
280            }
281            InstructionError::Custom(num) => {
282                write!(f, "custom program error: {num:#x}")
283            }
284            InstructionError::InvalidError => f.write_str("program returned invalid error code"),
285            InstructionError::ExecutableDataModified => {
286                f.write_str("instruction changed executable accounts data")
287            }
288            InstructionError::ExecutableLamportChange => {
289                f.write_str("instruction changed the balance of an executable account")
290            }
291            InstructionError::ExecutableAccountNotRentExempt => {
292                f.write_str("executable accounts must be rent exempt")
293            }
294            InstructionError::UnsupportedProgramId => f.write_str("Unsupported program id"),
295            InstructionError::CallDepth => {
296                f.write_str("Cross-program invocation call depth too deep")
297            }
298            InstructionError::MissingAccount => {
299                f.write_str("An account required by the instruction is missing")
300            }
301            InstructionError::ReentrancyNotAllowed => {
302                f.write_str("Cross-program invocation reentrancy not allowed for this instruction")
303            }
304            InstructionError::MaxSeedLengthExceeded => {
305                f.write_str("Length of the seed is too long for address generation")
306            }
307            InstructionError::InvalidSeeds => {
308                f.write_str("Provided seeds do not result in a valid address")
309            }
310            InstructionError::InvalidRealloc => f.write_str("Failed to reallocate account data"),
311            InstructionError::ComputationalBudgetExceeded => {
312                f.write_str("Computational budget exceeded")
313            }
314            InstructionError::PrivilegeEscalation => {
315                f.write_str("Cross-program invocation with unauthorized signer or writable account")
316            }
317            InstructionError::ProgramEnvironmentSetupFailure => {
318                f.write_str("Failed to create program execution environment")
319            }
320            InstructionError::ProgramFailedToComplete => f.write_str("Program failed to complete"),
321            InstructionError::ProgramFailedToCompile => f.write_str("Program failed to compile"),
322            InstructionError::Immutable => f.write_str("Account is immutable"),
323            InstructionError::IncorrectAuthority => f.write_str("Incorrect authority provided"),
324            InstructionError::BorshIoError => {
325                f.write_str("Failed to serialize or deserialize account data")
326            }
327            InstructionError::AccountNotRentExempt => {
328                f.write_str("An account does not have enough lamports to be rent-exempt")
329            }
330            InstructionError::InvalidAccountOwner => f.write_str("Invalid account owner"),
331            InstructionError::ArithmeticOverflow => f.write_str("Program arithmetic overflowed"),
332            InstructionError::UnsupportedSysvar => f.write_str("Unsupported sysvar"),
333            InstructionError::IllegalOwner => f.write_str("Provided owner is not allowed"),
334            InstructionError::MaxAccountsDataAllocationsExceeded => f.write_str(
335                "Accounts data allocations exceeded the maximum allowed per transaction",
336            ),
337            InstructionError::MaxAccountsExceeded => f.write_str("Max accounts exceeded"),
338            InstructionError::MaxInstructionTraceLengthExceeded => {
339                f.write_str("Max instruction trace length exceeded")
340            }
341            InstructionError::BuiltinProgramsMustConsumeComputeUnits => {
342                f.write_str("Builtin programs must consume compute units")
343            }
344        }
345    }
346}
347
348#[cfg(feature = "num-traits")]
349impl<T> From<T> for InstructionError
350where
351    T: ToPrimitive,
352{
353    fn from(error: T) -> Self {
354        let error = error.to_u64().unwrap_or(0xbad_c0de);
355        match error {
356            CUSTOM_ZERO => Self::Custom(0),
357            INVALID_ARGUMENT => Self::InvalidArgument,
358            INVALID_INSTRUCTION_DATA => Self::InvalidInstructionData,
359            INVALID_ACCOUNT_DATA => Self::InvalidAccountData,
360            ACCOUNT_DATA_TOO_SMALL => Self::AccountDataTooSmall,
361            INSUFFICIENT_FUNDS => Self::InsufficientFunds,
362            INCORRECT_PROGRAM_ID => Self::IncorrectProgramId,
363            MISSING_REQUIRED_SIGNATURES => Self::MissingRequiredSignature,
364            ACCOUNT_ALREADY_INITIALIZED => Self::AccountAlreadyInitialized,
365            UNINITIALIZED_ACCOUNT => Self::UninitializedAccount,
366            NOT_ENOUGH_ACCOUNT_KEYS => Self::NotEnoughAccountKeys,
367            ACCOUNT_BORROW_FAILED => Self::AccountBorrowFailed,
368            MAX_SEED_LENGTH_EXCEEDED => Self::MaxSeedLengthExceeded,
369            INVALID_SEEDS => Self::InvalidSeeds,
370            BORSH_IO_ERROR => Self::BorshIoError,
371            ACCOUNT_NOT_RENT_EXEMPT => Self::AccountNotRentExempt,
372            UNSUPPORTED_SYSVAR => Self::UnsupportedSysvar,
373            ILLEGAL_OWNER => Self::IllegalOwner,
374            MAX_ACCOUNTS_DATA_ALLOCATIONS_EXCEEDED => Self::MaxAccountsDataAllocationsExceeded,
375            INVALID_ACCOUNT_DATA_REALLOC => Self::InvalidRealloc,
376            MAX_INSTRUCTION_TRACE_LENGTH_EXCEEDED => Self::MaxInstructionTraceLengthExceeded,
377            BUILTIN_PROGRAMS_MUST_CONSUME_COMPUTE_UNITS => {
378                Self::BuiltinProgramsMustConsumeComputeUnits
379            }
380            INVALID_ACCOUNT_OWNER => Self::InvalidAccountOwner,
381            ARITHMETIC_OVERFLOW => Self::ArithmeticOverflow,
382            IMMUTABLE => Self::Immutable,
383            INCORRECT_AUTHORITY => Self::IncorrectAuthority,
384            _ => {
385                // A valid custom error has no bits set in the upper 32
386                if error >> solana_program_error::BUILTIN_BIT_SHIFT == 0 {
387                    Self::Custom(error as u32)
388                } else {
389                    Self::InvalidError
390                }
391            }
392        }
393    }
394}
395
396#[derive(Debug)]
397pub enum LamportsError {
398    /// arithmetic underflowed
399    ArithmeticUnderflow,
400    /// arithmetic overflowed
401    ArithmeticOverflow,
402}
403
404impl core::error::Error for LamportsError {}
405
406impl fmt::Display for LamportsError {
407    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
408        match self {
409            Self::ArithmeticUnderflow => f.write_str("Arithmetic underflowed"),
410            Self::ArithmeticOverflow => f.write_str("Arithmetic overflowed"),
411        }
412    }
413}
414
415impl From<LamportsError> for InstructionError {
416    fn from(error: LamportsError) -> Self {
417        match error {
418            LamportsError::ArithmeticOverflow => InstructionError::ArithmeticOverflow,
419            LamportsError::ArithmeticUnderflow => InstructionError::ArithmeticOverflow,
420        }
421    }
422}
423
424impl TryFrom<InstructionError> for ProgramError {
425    type Error = InstructionError;
426
427    fn try_from(error: InstructionError) -> Result<Self, Self::Error> {
428        match error {
429            Self::Error::Custom(err) => Ok(Self::Custom(err)),
430            Self::Error::InvalidArgument => Ok(Self::InvalidArgument),
431            Self::Error::InvalidInstructionData => Ok(Self::InvalidInstructionData),
432            Self::Error::InvalidAccountData => Ok(Self::InvalidAccountData),
433            Self::Error::AccountDataTooSmall => Ok(Self::AccountDataTooSmall),
434            Self::Error::InsufficientFunds => Ok(Self::InsufficientFunds),
435            Self::Error::IncorrectProgramId => Ok(Self::IncorrectProgramId),
436            Self::Error::MissingRequiredSignature => Ok(Self::MissingRequiredSignature),
437            Self::Error::AccountAlreadyInitialized => Ok(Self::AccountAlreadyInitialized),
438            Self::Error::UninitializedAccount => Ok(Self::UninitializedAccount),
439            Self::Error::NotEnoughAccountKeys => Ok(Self::NotEnoughAccountKeys),
440            Self::Error::AccountBorrowFailed => Ok(Self::AccountBorrowFailed),
441            Self::Error::MaxSeedLengthExceeded => Ok(Self::MaxSeedLengthExceeded),
442            Self::Error::InvalidSeeds => Ok(Self::InvalidSeeds),
443            Self::Error::BorshIoError => Ok(Self::BorshIoError),
444            Self::Error::AccountNotRentExempt => Ok(Self::AccountNotRentExempt),
445            Self::Error::UnsupportedSysvar => Ok(Self::UnsupportedSysvar),
446            Self::Error::IllegalOwner => Ok(Self::IllegalOwner),
447            Self::Error::MaxAccountsDataAllocationsExceeded => {
448                Ok(Self::MaxAccountsDataAllocationsExceeded)
449            }
450            Self::Error::InvalidRealloc => Ok(Self::InvalidRealloc),
451            Self::Error::MaxInstructionTraceLengthExceeded => {
452                Ok(Self::MaxInstructionTraceLengthExceeded)
453            }
454            Self::Error::BuiltinProgramsMustConsumeComputeUnits => {
455                Ok(Self::BuiltinProgramsMustConsumeComputeUnits)
456            }
457            Self::Error::InvalidAccountOwner => Ok(Self::InvalidAccountOwner),
458            Self::Error::ArithmeticOverflow => Ok(Self::ArithmeticOverflow),
459            Self::Error::Immutable => Ok(Self::Immutable),
460            Self::Error::IncorrectAuthority => Ok(Self::IncorrectAuthority),
461            _ => Err(error),
462        }
463    }
464}