1#![allow(clippy::integer_arithmetic)]
4use {
5 crate::{decode_error::DecodeError, instruction::InstructionError, msg, pubkey::PubkeyError},
6 borsh::maybestd::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 where 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("Account data allocation exceeded the maximum accounts data size limit")]
55 MaxAccountsDataSizeExceeded,
56 #[error("Account data reallocation was invalid")]
57 InvalidRealloc,
58}
59
60pub trait PrintProgramError {
61 fn print<E>(&self)
62 where
63 E: 'static + std::error::Error + DecodeError<E> + PrintProgramError + FromPrimitive;
64}
65
66impl PrintProgramError for ProgramError {
67 fn print<E>(&self)
68 where
69 E: 'static + std::error::Error + DecodeError<E> + PrintProgramError + FromPrimitive,
70 {
71 match self {
72 Self::Custom(error) => {
73 if let Some(custom_error) = E::decode_custom_error_to_enum(*error) {
74 custom_error.print::<E>();
75 } else {
76 msg!("Error: Unknown");
77 }
78 }
79 Self::InvalidArgument => msg!("Error: InvalidArgument"),
80 Self::InvalidInstructionData => msg!("Error: InvalidInstructionData"),
81 Self::InvalidAccountData => msg!("Error: InvalidAccountData"),
82 Self::AccountDataTooSmall => msg!("Error: AccountDataTooSmall"),
83 Self::InsufficientFunds => msg!("Error: InsufficientFunds"),
84 Self::IncorrectProgramId => msg!("Error: IncorrectProgramId"),
85 Self::MissingRequiredSignature => msg!("Error: MissingRequiredSignature"),
86 Self::AccountAlreadyInitialized => msg!("Error: AccountAlreadyInitialized"),
87 Self::UninitializedAccount => msg!("Error: UninitializedAccount"),
88 Self::NotEnoughAccountKeys => msg!("Error: NotEnoughAccountKeys"),
89 Self::AccountBorrowFailed => msg!("Error: AccountBorrowFailed"),
90 Self::MaxSeedLengthExceeded => msg!("Error: MaxSeedLengthExceeded"),
91 Self::InvalidSeeds => msg!("Error: InvalidSeeds"),
92 Self::BorshIoError(_) => msg!("Error: BorshIoError"),
93 Self::AccountNotRentExempt => msg!("Error: AccountNotRentExempt"),
94 Self::UnsupportedSysvar => msg!("Error: UnsupportedSysvar"),
95 Self::IllegalOwner => msg!("Error: IllegalOwner"),
96 Self::MaxAccountsDataSizeExceeded => msg!("Error: MaxAccountsDataSizeExceeded"),
97 Self::InvalidRealloc => msg!("Error: InvalidRealloc"),
98 }
99 }
100}
101
102const BUILTIN_BIT_SHIFT: usize = 32;
104macro_rules! to_builtin {
105 ($error:expr) => {
106 ($error as u64) << BUILTIN_BIT_SHIFT
107 };
108}
109
110pub const CUSTOM_ZERO: u64 = to_builtin!(1);
111pub const INVALID_ARGUMENT: u64 = to_builtin!(2);
112pub const INVALID_INSTRUCTION_DATA: u64 = to_builtin!(3);
113pub const INVALID_ACCOUNT_DATA: u64 = to_builtin!(4);
114pub const ACCOUNT_DATA_TOO_SMALL: u64 = to_builtin!(5);
115pub const INSUFFICIENT_FUNDS: u64 = to_builtin!(6);
116pub const INCORRECT_PROGRAM_ID: u64 = to_builtin!(7);
117pub const MISSING_REQUIRED_SIGNATURES: u64 = to_builtin!(8);
118pub const ACCOUNT_ALREADY_INITIALIZED: u64 = to_builtin!(9);
119pub const UNINITIALIZED_ACCOUNT: u64 = to_builtin!(10);
120pub const NOT_ENOUGH_ACCOUNT_KEYS: u64 = to_builtin!(11);
121pub const ACCOUNT_BORROW_FAILED: u64 = to_builtin!(12);
122pub const MAX_SEED_LENGTH_EXCEEDED: u64 = to_builtin!(13);
123pub const INVALID_SEEDS: u64 = to_builtin!(14);
124pub const BORSH_IO_ERROR: u64 = to_builtin!(15);
125pub const ACCOUNT_NOT_RENT_EXEMPT: u64 = to_builtin!(16);
126pub const UNSUPPORTED_SYSVAR: u64 = to_builtin!(17);
127pub const ILLEGAL_OWNER: u64 = to_builtin!(18);
128pub const MAX_ACCOUNTS_DATA_SIZE_EXCEEDED: u64 = to_builtin!(19);
129pub const INVALID_ACCOUNT_DATA_REALLOC: u64 = to_builtin!(20);
130impl From<ProgramError> for u64 {
137 fn from(error: ProgramError) -> Self {
138 match error {
139 ProgramError::InvalidArgument => INVALID_ARGUMENT,
140 ProgramError::InvalidInstructionData => INVALID_INSTRUCTION_DATA,
141 ProgramError::InvalidAccountData => INVALID_ACCOUNT_DATA,
142 ProgramError::AccountDataTooSmall => ACCOUNT_DATA_TOO_SMALL,
143 ProgramError::InsufficientFunds => INSUFFICIENT_FUNDS,
144 ProgramError::IncorrectProgramId => INCORRECT_PROGRAM_ID,
145 ProgramError::MissingRequiredSignature => MISSING_REQUIRED_SIGNATURES,
146 ProgramError::AccountAlreadyInitialized => ACCOUNT_ALREADY_INITIALIZED,
147 ProgramError::UninitializedAccount => UNINITIALIZED_ACCOUNT,
148 ProgramError::NotEnoughAccountKeys => NOT_ENOUGH_ACCOUNT_KEYS,
149 ProgramError::AccountBorrowFailed => ACCOUNT_BORROW_FAILED,
150 ProgramError::MaxSeedLengthExceeded => MAX_SEED_LENGTH_EXCEEDED,
151 ProgramError::InvalidSeeds => INVALID_SEEDS,
152 ProgramError::BorshIoError(_) => BORSH_IO_ERROR,
153 ProgramError::AccountNotRentExempt => ACCOUNT_NOT_RENT_EXEMPT,
154 ProgramError::UnsupportedSysvar => UNSUPPORTED_SYSVAR,
155 ProgramError::IllegalOwner => ILLEGAL_OWNER,
156 ProgramError::MaxAccountsDataSizeExceeded => MAX_ACCOUNTS_DATA_SIZE_EXCEEDED,
157 ProgramError::InvalidRealloc => INVALID_ACCOUNT_DATA_REALLOC,
158 ProgramError::Custom(error) => {
159 if error == 0 {
160 CUSTOM_ZERO
161 } else {
162 error as u64
163 }
164 }
165 }
166 }
167}
168
169impl From<u64> for ProgramError {
170 fn from(error: u64) -> Self {
171 match error {
172 CUSTOM_ZERO => Self::Custom(0),
173 INVALID_ARGUMENT => Self::InvalidArgument,
174 INVALID_INSTRUCTION_DATA => Self::InvalidInstructionData,
175 INVALID_ACCOUNT_DATA => Self::InvalidAccountData,
176 ACCOUNT_DATA_TOO_SMALL => Self::AccountDataTooSmall,
177 INSUFFICIENT_FUNDS => Self::InsufficientFunds,
178 INCORRECT_PROGRAM_ID => Self::IncorrectProgramId,
179 MISSING_REQUIRED_SIGNATURES => Self::MissingRequiredSignature,
180 ACCOUNT_ALREADY_INITIALIZED => Self::AccountAlreadyInitialized,
181 UNINITIALIZED_ACCOUNT => Self::UninitializedAccount,
182 NOT_ENOUGH_ACCOUNT_KEYS => Self::NotEnoughAccountKeys,
183 ACCOUNT_BORROW_FAILED => Self::AccountBorrowFailed,
184 MAX_SEED_LENGTH_EXCEEDED => Self::MaxSeedLengthExceeded,
185 INVALID_SEEDS => Self::InvalidSeeds,
186 BORSH_IO_ERROR => Self::BorshIoError("Unknown".to_string()),
187 ACCOUNT_NOT_RENT_EXEMPT => Self::AccountNotRentExempt,
188 UNSUPPORTED_SYSVAR => Self::UnsupportedSysvar,
189 ILLEGAL_OWNER => Self::IllegalOwner,
190 MAX_ACCOUNTS_DATA_SIZE_EXCEEDED => Self::MaxAccountsDataSizeExceeded,
191 INVALID_ACCOUNT_DATA_REALLOC => Self::InvalidRealloc,
192 _ => Self::Custom(error as u32),
193 }
194 }
195}
196
197impl TryFrom<InstructionError> for ProgramError {
198 type Error = InstructionError;
199
200 fn try_from(error: InstructionError) -> Result<Self, Self::Error> {
201 match error {
202 Self::Error::Custom(err) => Ok(Self::Custom(err)),
203 Self::Error::InvalidArgument => Ok(Self::InvalidArgument),
204 Self::Error::InvalidInstructionData => Ok(Self::InvalidInstructionData),
205 Self::Error::InvalidAccountData => Ok(Self::InvalidAccountData),
206 Self::Error::AccountDataTooSmall => Ok(Self::AccountDataTooSmall),
207 Self::Error::InsufficientFunds => Ok(Self::InsufficientFunds),
208 Self::Error::IncorrectProgramId => Ok(Self::IncorrectProgramId),
209 Self::Error::MissingRequiredSignature => Ok(Self::MissingRequiredSignature),
210 Self::Error::AccountAlreadyInitialized => Ok(Self::AccountAlreadyInitialized),
211 Self::Error::UninitializedAccount => Ok(Self::UninitializedAccount),
212 Self::Error::NotEnoughAccountKeys => Ok(Self::NotEnoughAccountKeys),
213 Self::Error::AccountBorrowFailed => Ok(Self::AccountBorrowFailed),
214 Self::Error::MaxSeedLengthExceeded => Ok(Self::MaxSeedLengthExceeded),
215 Self::Error::InvalidSeeds => Ok(Self::InvalidSeeds),
216 Self::Error::BorshIoError(err) => Ok(Self::BorshIoError(err)),
217 Self::Error::AccountNotRentExempt => Ok(Self::AccountNotRentExempt),
218 Self::Error::UnsupportedSysvar => Ok(Self::UnsupportedSysvar),
219 Self::Error::IllegalOwner => Ok(Self::IllegalOwner),
220 Self::Error::MaxAccountsDataSizeExceeded => Ok(Self::MaxAccountsDataSizeExceeded),
221 Self::Error::InvalidRealloc => Ok(Self::InvalidRealloc),
222 _ => Err(error),
223 }
224 }
225}
226
227impl<T> From<T> for InstructionError
228where
229 T: ToPrimitive,
230{
231 fn from(error: T) -> Self {
232 let error = error.to_u64().unwrap_or(0xbad_c0de);
233 match error {
234 CUSTOM_ZERO => Self::Custom(0),
235 INVALID_ARGUMENT => Self::InvalidArgument,
236 INVALID_INSTRUCTION_DATA => Self::InvalidInstructionData,
237 INVALID_ACCOUNT_DATA => Self::InvalidAccountData,
238 ACCOUNT_DATA_TOO_SMALL => Self::AccountDataTooSmall,
239 INSUFFICIENT_FUNDS => Self::InsufficientFunds,
240 INCORRECT_PROGRAM_ID => Self::IncorrectProgramId,
241 MISSING_REQUIRED_SIGNATURES => Self::MissingRequiredSignature,
242 ACCOUNT_ALREADY_INITIALIZED => Self::AccountAlreadyInitialized,
243 UNINITIALIZED_ACCOUNT => Self::UninitializedAccount,
244 NOT_ENOUGH_ACCOUNT_KEYS => Self::NotEnoughAccountKeys,
245 ACCOUNT_BORROW_FAILED => Self::AccountBorrowFailed,
246 MAX_SEED_LENGTH_EXCEEDED => Self::MaxSeedLengthExceeded,
247 INVALID_SEEDS => Self::InvalidSeeds,
248 BORSH_IO_ERROR => Self::BorshIoError("Unknown".to_string()),
249 ACCOUNT_NOT_RENT_EXEMPT => Self::AccountNotRentExempt,
250 UNSUPPORTED_SYSVAR => Self::UnsupportedSysvar,
251 ILLEGAL_OWNER => Self::IllegalOwner,
252 MAX_ACCOUNTS_DATA_SIZE_EXCEEDED => Self::MaxAccountsDataSizeExceeded,
253 INVALID_ACCOUNT_DATA_REALLOC => Self::InvalidRealloc,
254 _ => {
255 if error >> BUILTIN_BIT_SHIFT == 0 {
257 Self::Custom(error as u32)
258 } else {
259 Self::InvalidError
260 }
261 }
262 }
263 }
264}
265
266impl From<PubkeyError> for ProgramError {
267 fn from(error: PubkeyError) -> Self {
268 match error {
269 PubkeyError::MaxSeedLengthExceeded => Self::MaxSeedLengthExceeded,
270 PubkeyError::InvalidSeeds => Self::InvalidSeeds,
271 PubkeyError::IllegalOwner => Self::IllegalOwner,
272 }
273 }
274}
275
276impl From<BorshIoError> for ProgramError {
277 fn from(error: BorshIoError) -> Self {
278 Self::BorshIoError(format!("{}", error))
279 }
280}