miraland_program/
program_error.rs

1//! The [`ProgramError`] type and related definitions.
2
3#![allow(clippy::arithmetic_side_effects)]
4use {
5    crate::{decode_error::DecodeError, instruction::InstructionError, msg, pubkey::PubkeyError},
6    borsh::io::Error as BorshIoError,
7    num_traits::{FromPrimitive, ToPrimitive},
8    std::convert::TryFrom,
9    thiserror::Error,
10};
11
12/// Reasons the program may fail
13#[derive(Clone, Debug, Deserialize, Eq, Error, PartialEq, Serialize)]
14pub enum ProgramError {
15    /// Allows on-chain programs to implement program-specific error types and see them returned
16    /// by the Miraland runtime. A program-specific error may be any type that is represented as
17    /// or serialized to a u32 integer.
18    #[error("Custom program error: {0:#x}")]
19    Custom(u32),
20    #[error("The arguments provided to a program instruction were invalid")]
21    InvalidArgument,
22    #[error("An instruction's data contents was invalid")]
23    InvalidInstructionData,
24    #[error("An account's data contents was invalid")]
25    InvalidAccountData,
26    #[error("An account's data was too small")]
27    AccountDataTooSmall,
28    #[error("An account's balance was too small to complete the instruction")]
29    InsufficientFunds,
30    #[error("The account did not have the expected program id")]
31    IncorrectProgramId,
32    #[error("A signature was required but not found")]
33    MissingRequiredSignature,
34    #[error("An initialize instruction was sent to an account that has already been initialized")]
35    AccountAlreadyInitialized,
36    #[error("An attempt to operate on an account that hasn't been initialized")]
37    UninitializedAccount,
38    #[error("The instruction expected additional account keys")]
39    NotEnoughAccountKeys,
40    #[error("Failed to borrow a reference to account data, already borrowed")]
41    AccountBorrowFailed,
42    #[error("Length of the seed is too long for address generation")]
43    MaxSeedLengthExceeded,
44    #[error("Provided seeds do not result in a valid address")]
45    InvalidSeeds,
46    #[error("IO Error: {0}")]
47    BorshIoError(String),
48    #[error("An account does not have enough lamports to be rent-exempt")]
49    AccountNotRentExempt,
50    #[error("Unsupported sysvar")]
51    UnsupportedSysvar,
52    #[error("Provided owner is not allowed")]
53    IllegalOwner,
54    #[error("Accounts data allocations exceeded the maximum allowed per transaction")]
55    MaxAccountsDataAllocationsExceeded,
56    #[error("Account data reallocation was invalid")]
57    InvalidRealloc,
58    #[error("Instruction trace length exceeded the maximum allowed per transaction")]
59    MaxInstructionTraceLengthExceeded,
60    #[error("Builtin programs must consume compute units")]
61    BuiltinProgramsMustConsumeComputeUnits,
62    #[error("Invalid account owner")]
63    InvalidAccountOwner,
64    #[error("Program arithmetic overflowed")]
65    ArithmeticOverflow,
66    #[error("Account is immutable")]
67    Immutable,
68    #[error("Incorrect authority provided")]
69    IncorrectAuthority,
70}
71
72pub trait PrintProgramError {
73    fn print<E>(&self)
74    where
75        E: 'static + std::error::Error + DecodeError<E> + PrintProgramError + FromPrimitive;
76}
77
78impl PrintProgramError for ProgramError {
79    fn print<E>(&self)
80    where
81        E: 'static + std::error::Error + DecodeError<E> + PrintProgramError + FromPrimitive,
82    {
83        match self {
84            Self::Custom(error) => {
85                if let Some(custom_error) = E::decode_custom_error_to_enum(*error) {
86                    custom_error.print::<E>();
87                } else {
88                    msg!("Error: Unknown");
89                }
90            }
91            Self::InvalidArgument => msg!("Error: InvalidArgument"),
92            Self::InvalidInstructionData => msg!("Error: InvalidInstructionData"),
93            Self::InvalidAccountData => msg!("Error: InvalidAccountData"),
94            Self::AccountDataTooSmall => msg!("Error: AccountDataTooSmall"),
95            Self::InsufficientFunds => msg!("Error: InsufficientFunds"),
96            Self::IncorrectProgramId => msg!("Error: IncorrectProgramId"),
97            Self::MissingRequiredSignature => msg!("Error: MissingRequiredSignature"),
98            Self::AccountAlreadyInitialized => msg!("Error: AccountAlreadyInitialized"),
99            Self::UninitializedAccount => msg!("Error: UninitializedAccount"),
100            Self::NotEnoughAccountKeys => msg!("Error: NotEnoughAccountKeys"),
101            Self::AccountBorrowFailed => msg!("Error: AccountBorrowFailed"),
102            Self::MaxSeedLengthExceeded => msg!("Error: MaxSeedLengthExceeded"),
103            Self::InvalidSeeds => msg!("Error: InvalidSeeds"),
104            Self::BorshIoError(_) => msg!("Error: BorshIoError"),
105            Self::AccountNotRentExempt => msg!("Error: AccountNotRentExempt"),
106            Self::UnsupportedSysvar => msg!("Error: UnsupportedSysvar"),
107            Self::IllegalOwner => msg!("Error: IllegalOwner"),
108            Self::MaxAccountsDataAllocationsExceeded => {
109                msg!("Error: MaxAccountsDataAllocationsExceeded")
110            }
111            Self::InvalidRealloc => msg!("Error: InvalidRealloc"),
112            Self::MaxInstructionTraceLengthExceeded => {
113                msg!("Error: MaxInstructionTraceLengthExceeded")
114            }
115            Self::BuiltinProgramsMustConsumeComputeUnits => {
116                msg!("Error: BuiltinProgramsMustConsumeComputeUnits")
117            }
118            Self::InvalidAccountOwner => msg!("Error: InvalidAccountOwner"),
119            Self::ArithmeticOverflow => msg!("Error: ArithmeticOverflow"),
120            Self::Immutable => msg!("Error: Immutable"),
121            Self::IncorrectAuthority => msg!("Error: IncorrectAuthority"),
122        }
123    }
124}
125
126/// Builtin return values occupy the upper 32 bits
127const BUILTIN_BIT_SHIFT: usize = 32;
128macro_rules! to_builtin {
129    ($error:expr) => {
130        ($error as u64) << BUILTIN_BIT_SHIFT
131    };
132}
133
134pub const CUSTOM_ZERO: u64 = to_builtin!(1);
135pub const INVALID_ARGUMENT: u64 = to_builtin!(2);
136pub const INVALID_INSTRUCTION_DATA: u64 = to_builtin!(3);
137pub const INVALID_ACCOUNT_DATA: u64 = to_builtin!(4);
138pub const ACCOUNT_DATA_TOO_SMALL: u64 = to_builtin!(5);
139pub const INSUFFICIENT_FUNDS: u64 = to_builtin!(6);
140pub const INCORRECT_PROGRAM_ID: u64 = to_builtin!(7);
141pub const MISSING_REQUIRED_SIGNATURES: u64 = to_builtin!(8);
142pub const ACCOUNT_ALREADY_INITIALIZED: u64 = to_builtin!(9);
143pub const UNINITIALIZED_ACCOUNT: u64 = to_builtin!(10);
144pub const NOT_ENOUGH_ACCOUNT_KEYS: u64 = to_builtin!(11);
145pub const ACCOUNT_BORROW_FAILED: u64 = to_builtin!(12);
146pub const MAX_SEED_LENGTH_EXCEEDED: u64 = to_builtin!(13);
147pub const INVALID_SEEDS: u64 = to_builtin!(14);
148pub const BORSH_IO_ERROR: u64 = to_builtin!(15);
149pub const ACCOUNT_NOT_RENT_EXEMPT: u64 = to_builtin!(16);
150pub const UNSUPPORTED_SYSVAR: u64 = to_builtin!(17);
151pub const ILLEGAL_OWNER: u64 = to_builtin!(18);
152pub const MAX_ACCOUNTS_DATA_ALLOCATIONS_EXCEEDED: u64 = to_builtin!(19);
153pub const INVALID_ACCOUNT_DATA_REALLOC: u64 = to_builtin!(20);
154pub const MAX_INSTRUCTION_TRACE_LENGTH_EXCEEDED: u64 = to_builtin!(21);
155pub const BUILTIN_PROGRAMS_MUST_CONSUME_COMPUTE_UNITS: u64 = to_builtin!(22);
156pub const INVALID_ACCOUNT_OWNER: u64 = to_builtin!(23);
157pub const ARITHMETIC_OVERFLOW: u64 = to_builtin!(24);
158pub const IMMUTABLE: u64 = to_builtin!(25);
159pub const INCORRECT_AUTHORITY: u64 = to_builtin!(26);
160// Warning: Any new program errors added here must also be:
161// - Added to the below conversions
162// - Added as an equivalent to InstructionError
163// - Be featureized in the BPF loader to return `InstructionError::InvalidError`
164//   until the feature is activated
165
166impl From<ProgramError> for u64 {
167    fn from(error: ProgramError) -> Self {
168        match error {
169            ProgramError::InvalidArgument => INVALID_ARGUMENT,
170            ProgramError::InvalidInstructionData => INVALID_INSTRUCTION_DATA,
171            ProgramError::InvalidAccountData => INVALID_ACCOUNT_DATA,
172            ProgramError::AccountDataTooSmall => ACCOUNT_DATA_TOO_SMALL,
173            ProgramError::InsufficientFunds => INSUFFICIENT_FUNDS,
174            ProgramError::IncorrectProgramId => INCORRECT_PROGRAM_ID,
175            ProgramError::MissingRequiredSignature => MISSING_REQUIRED_SIGNATURES,
176            ProgramError::AccountAlreadyInitialized => ACCOUNT_ALREADY_INITIALIZED,
177            ProgramError::UninitializedAccount => UNINITIALIZED_ACCOUNT,
178            ProgramError::NotEnoughAccountKeys => NOT_ENOUGH_ACCOUNT_KEYS,
179            ProgramError::AccountBorrowFailed => ACCOUNT_BORROW_FAILED,
180            ProgramError::MaxSeedLengthExceeded => MAX_SEED_LENGTH_EXCEEDED,
181            ProgramError::InvalidSeeds => INVALID_SEEDS,
182            ProgramError::BorshIoError(_) => BORSH_IO_ERROR,
183            ProgramError::AccountNotRentExempt => ACCOUNT_NOT_RENT_EXEMPT,
184            ProgramError::UnsupportedSysvar => UNSUPPORTED_SYSVAR,
185            ProgramError::IllegalOwner => ILLEGAL_OWNER,
186            ProgramError::MaxAccountsDataAllocationsExceeded => {
187                MAX_ACCOUNTS_DATA_ALLOCATIONS_EXCEEDED
188            }
189            ProgramError::InvalidRealloc => INVALID_ACCOUNT_DATA_REALLOC,
190            ProgramError::MaxInstructionTraceLengthExceeded => {
191                MAX_INSTRUCTION_TRACE_LENGTH_EXCEEDED
192            }
193            ProgramError::BuiltinProgramsMustConsumeComputeUnits => {
194                BUILTIN_PROGRAMS_MUST_CONSUME_COMPUTE_UNITS
195            }
196            ProgramError::InvalidAccountOwner => INVALID_ACCOUNT_OWNER,
197            ProgramError::ArithmeticOverflow => ARITHMETIC_OVERFLOW,
198            ProgramError::Immutable => IMMUTABLE,
199            ProgramError::IncorrectAuthority => INCORRECT_AUTHORITY,
200            ProgramError::Custom(error) => {
201                if error == 0 {
202                    CUSTOM_ZERO
203                } else {
204                    error as u64
205                }
206            }
207        }
208    }
209}
210
211impl From<u64> for ProgramError {
212    fn from(error: u64) -> Self {
213        match error {
214            CUSTOM_ZERO => Self::Custom(0),
215            INVALID_ARGUMENT => Self::InvalidArgument,
216            INVALID_INSTRUCTION_DATA => Self::InvalidInstructionData,
217            INVALID_ACCOUNT_DATA => Self::InvalidAccountData,
218            ACCOUNT_DATA_TOO_SMALL => Self::AccountDataTooSmall,
219            INSUFFICIENT_FUNDS => Self::InsufficientFunds,
220            INCORRECT_PROGRAM_ID => Self::IncorrectProgramId,
221            MISSING_REQUIRED_SIGNATURES => Self::MissingRequiredSignature,
222            ACCOUNT_ALREADY_INITIALIZED => Self::AccountAlreadyInitialized,
223            UNINITIALIZED_ACCOUNT => Self::UninitializedAccount,
224            NOT_ENOUGH_ACCOUNT_KEYS => Self::NotEnoughAccountKeys,
225            ACCOUNT_BORROW_FAILED => Self::AccountBorrowFailed,
226            MAX_SEED_LENGTH_EXCEEDED => Self::MaxSeedLengthExceeded,
227            INVALID_SEEDS => Self::InvalidSeeds,
228            BORSH_IO_ERROR => Self::BorshIoError("Unknown".to_string()),
229            ACCOUNT_NOT_RENT_EXEMPT => Self::AccountNotRentExempt,
230            UNSUPPORTED_SYSVAR => Self::UnsupportedSysvar,
231            ILLEGAL_OWNER => Self::IllegalOwner,
232            MAX_ACCOUNTS_DATA_ALLOCATIONS_EXCEEDED => Self::MaxAccountsDataAllocationsExceeded,
233            INVALID_ACCOUNT_DATA_REALLOC => Self::InvalidRealloc,
234            MAX_INSTRUCTION_TRACE_LENGTH_EXCEEDED => Self::MaxInstructionTraceLengthExceeded,
235            BUILTIN_PROGRAMS_MUST_CONSUME_COMPUTE_UNITS => {
236                Self::BuiltinProgramsMustConsumeComputeUnits
237            }
238            INVALID_ACCOUNT_OWNER => Self::InvalidAccountOwner,
239            ARITHMETIC_OVERFLOW => Self::ArithmeticOverflow,
240            IMMUTABLE => Self::Immutable,
241            INCORRECT_AUTHORITY => Self::IncorrectAuthority,
242            _ => Self::Custom(error as u32),
243        }
244    }
245}
246
247impl TryFrom<InstructionError> for ProgramError {
248    type Error = InstructionError;
249
250    fn try_from(error: InstructionError) -> Result<Self, Self::Error> {
251        match error {
252            Self::Error::Custom(err) => Ok(Self::Custom(err)),
253            Self::Error::InvalidArgument => Ok(Self::InvalidArgument),
254            Self::Error::InvalidInstructionData => Ok(Self::InvalidInstructionData),
255            Self::Error::InvalidAccountData => Ok(Self::InvalidAccountData),
256            Self::Error::AccountDataTooSmall => Ok(Self::AccountDataTooSmall),
257            Self::Error::InsufficientFunds => Ok(Self::InsufficientFunds),
258            Self::Error::IncorrectProgramId => Ok(Self::IncorrectProgramId),
259            Self::Error::MissingRequiredSignature => Ok(Self::MissingRequiredSignature),
260            Self::Error::AccountAlreadyInitialized => Ok(Self::AccountAlreadyInitialized),
261            Self::Error::UninitializedAccount => Ok(Self::UninitializedAccount),
262            Self::Error::NotEnoughAccountKeys => Ok(Self::NotEnoughAccountKeys),
263            Self::Error::AccountBorrowFailed => Ok(Self::AccountBorrowFailed),
264            Self::Error::MaxSeedLengthExceeded => Ok(Self::MaxSeedLengthExceeded),
265            Self::Error::InvalidSeeds => Ok(Self::InvalidSeeds),
266            Self::Error::BorshIoError(err) => Ok(Self::BorshIoError(err)),
267            Self::Error::AccountNotRentExempt => Ok(Self::AccountNotRentExempt),
268            Self::Error::UnsupportedSysvar => Ok(Self::UnsupportedSysvar),
269            Self::Error::IllegalOwner => Ok(Self::IllegalOwner),
270            Self::Error::MaxAccountsDataAllocationsExceeded => {
271                Ok(Self::MaxAccountsDataAllocationsExceeded)
272            }
273            Self::Error::InvalidRealloc => Ok(Self::InvalidRealloc),
274            Self::Error::MaxInstructionTraceLengthExceeded => {
275                Ok(Self::MaxInstructionTraceLengthExceeded)
276            }
277            Self::Error::BuiltinProgramsMustConsumeComputeUnits => {
278                Ok(Self::BuiltinProgramsMustConsumeComputeUnits)
279            }
280            Self::Error::InvalidAccountOwner => Ok(Self::InvalidAccountOwner),
281            Self::Error::ArithmeticOverflow => Ok(Self::ArithmeticOverflow),
282            Self::Error::Immutable => Ok(Self::Immutable),
283            Self::Error::IncorrectAuthority => Ok(Self::IncorrectAuthority),
284            _ => Err(error),
285        }
286    }
287}
288
289impl<T> From<T> for InstructionError
290where
291    T: ToPrimitive,
292{
293    fn from(error: T) -> Self {
294        let error = error.to_u64().unwrap_or(0xbad_c0de);
295        match error {
296            CUSTOM_ZERO => Self::Custom(0),
297            INVALID_ARGUMENT => Self::InvalidArgument,
298            INVALID_INSTRUCTION_DATA => Self::InvalidInstructionData,
299            INVALID_ACCOUNT_DATA => Self::InvalidAccountData,
300            ACCOUNT_DATA_TOO_SMALL => Self::AccountDataTooSmall,
301            INSUFFICIENT_FUNDS => Self::InsufficientFunds,
302            INCORRECT_PROGRAM_ID => Self::IncorrectProgramId,
303            MISSING_REQUIRED_SIGNATURES => Self::MissingRequiredSignature,
304            ACCOUNT_ALREADY_INITIALIZED => Self::AccountAlreadyInitialized,
305            UNINITIALIZED_ACCOUNT => Self::UninitializedAccount,
306            NOT_ENOUGH_ACCOUNT_KEYS => Self::NotEnoughAccountKeys,
307            ACCOUNT_BORROW_FAILED => Self::AccountBorrowFailed,
308            MAX_SEED_LENGTH_EXCEEDED => Self::MaxSeedLengthExceeded,
309            INVALID_SEEDS => Self::InvalidSeeds,
310            BORSH_IO_ERROR => Self::BorshIoError("Unknown".to_string()),
311            ACCOUNT_NOT_RENT_EXEMPT => Self::AccountNotRentExempt,
312            UNSUPPORTED_SYSVAR => Self::UnsupportedSysvar,
313            ILLEGAL_OWNER => Self::IllegalOwner,
314            MAX_ACCOUNTS_DATA_ALLOCATIONS_EXCEEDED => Self::MaxAccountsDataAllocationsExceeded,
315            INVALID_ACCOUNT_DATA_REALLOC => Self::InvalidRealloc,
316            MAX_INSTRUCTION_TRACE_LENGTH_EXCEEDED => Self::MaxInstructionTraceLengthExceeded,
317            BUILTIN_PROGRAMS_MUST_CONSUME_COMPUTE_UNITS => {
318                Self::BuiltinProgramsMustConsumeComputeUnits
319            }
320            INVALID_ACCOUNT_OWNER => Self::InvalidAccountOwner,
321            ARITHMETIC_OVERFLOW => Self::ArithmeticOverflow,
322            IMMUTABLE => Self::Immutable,
323            INCORRECT_AUTHORITY => Self::IncorrectAuthority,
324            _ => {
325                // A valid custom error has no bits set in the upper 32
326                if error >> BUILTIN_BIT_SHIFT == 0 {
327                    Self::Custom(error as u32)
328                } else {
329                    Self::InvalidError
330                }
331            }
332        }
333    }
334}
335
336impl From<PubkeyError> for ProgramError {
337    fn from(error: PubkeyError) -> Self {
338        match error {
339            PubkeyError::MaxSeedLengthExceeded => Self::MaxSeedLengthExceeded,
340            PubkeyError::InvalidSeeds => Self::InvalidSeeds,
341            PubkeyError::IllegalOwner => Self::IllegalOwner,
342        }
343    }
344}
345
346impl From<BorshIoError> for ProgramError {
347    fn from(error: BorshIoError) -> Self {
348        Self::BorshIoError(format!("{error}"))
349    }
350}