1#![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#[derive(Clone, Debug, Deserialize, Eq, Error, PartialEq, Serialize)]
14pub enum ProgramError {
15 #[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
126const 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);
160impl 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 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}