solana_instruction_error/
lib.rs

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