gemachain_program/
program_error.rs

1#![allow(clippy::integer_arithmetic)]
2use crate::{decode_error::DecodeError, instruction::InstructionError, msg, pubkey::PubkeyError};
3use borsh::maybestd::io::Error as BorshIoError;
4use num_traits::{FromPrimitive, ToPrimitive};
5use std::convert::TryFrom;
6use thiserror::Error;
7
8/// Reasons the program may fail
9#[derive(Clone, Debug, Deserialize, Eq, Error, PartialEq, Serialize)]
10pub enum ProgramError {
11    /// Allows on-chain programs to implement program-specific error types and see them returned
12    /// by the Gemachain runtime. A program-specific error may be any type that is represented as
13    /// or serialized to a u32 integer.
14    #[error("Custom program error: {0:#x}")]
15    Custom(u32),
16    #[error("The arguments provided to a program instruction where invalid")]
17    InvalidArgument,
18    #[error("An instruction's data contents was invalid")]
19    InvalidInstructionData,
20    #[error("An account's data contents was invalid")]
21    InvalidAccountData,
22    #[error("An account's data was too small")]
23    AccountDataTooSmall,
24    #[error("An account's balance was too small to complete the instruction")]
25    InsufficientFunds,
26    #[error("The account did not have the expected program id")]
27    IncorrectProgramId,
28    #[error("A signature was required but not found")]
29    MissingRequiredSignature,
30    #[error("An initialize instruction was sent to an account that has already been initialized")]
31    AccountAlreadyInitialized,
32    #[error("An attempt to operate on an account that hasn't been initialized")]
33    UninitializedAccount,
34    #[error("The instruction expected additional account keys")]
35    NotEnoughAccountKeys,
36    #[error("Failed to borrow a reference to account data, already borrowed")]
37    AccountBorrowFailed,
38    #[error("Length of the seed is too long for address generation")]
39    MaxSeedLengthExceeded,
40    #[error("Provided seeds do not result in a valid address")]
41    InvalidSeeds,
42    #[error("IO Error: {0}")]
43    BorshIoError(String),
44    #[error("An account does not have enough carats to be rent-exempt")]
45    AccountNotRentExempt,
46    #[error("Unsupported sysvar")]
47    UnsupportedSysvar,
48    #[error("Provided owner is not allowed")]
49    IllegalOwner,
50}
51
52pub trait PrintProgramError {
53    fn print<E>(&self)
54    where
55        E: 'static + std::error::Error + DecodeError<E> + PrintProgramError + FromPrimitive;
56}
57
58impl PrintProgramError for ProgramError {
59    fn print<E>(&self)
60    where
61        E: 'static + std::error::Error + DecodeError<E> + PrintProgramError + FromPrimitive,
62    {
63        match self {
64            Self::Custom(error) => {
65                if let Some(custom_error) = E::decode_custom_error_to_enum(*error) {
66                    custom_error.print::<E>();
67                } else {
68                    msg!("Error: Unknown");
69                }
70            }
71            Self::InvalidArgument => msg!("Error: InvalidArgument"),
72            Self::InvalidInstructionData => msg!("Error: InvalidInstructionData"),
73            Self::InvalidAccountData => msg!("Error: InvalidAccountData"),
74            Self::AccountDataTooSmall => msg!("Error: AccountDataTooSmall"),
75            Self::InsufficientFunds => msg!("Error: InsufficientFunds"),
76            Self::IncorrectProgramId => msg!("Error: IncorrectProgramId"),
77            Self::MissingRequiredSignature => msg!("Error: MissingRequiredSignature"),
78            Self::AccountAlreadyInitialized => msg!("Error: AccountAlreadyInitialized"),
79            Self::UninitializedAccount => msg!("Error: UninitializedAccount"),
80            Self::NotEnoughAccountKeys => msg!("Error: NotEnoughAccountKeys"),
81            Self::AccountBorrowFailed => msg!("Error: AccountBorrowFailed"),
82            Self::MaxSeedLengthExceeded => msg!("Error: MaxSeedLengthExceeded"),
83            Self::InvalidSeeds => msg!("Error: InvalidSeeds"),
84            Self::BorshIoError(_) => msg!("Error: BorshIoError"),
85            Self::AccountNotRentExempt => msg!("Error: AccountNotRentExempt"),
86            Self::UnsupportedSysvar => msg!("Error: UnsupportedSysvar"),
87            Self::IllegalOwner => msg!("Error: IllegalOwner"),
88        }
89    }
90}
91
92/// Builtin return values occupy the upper 32 bits
93const BUILTIN_BIT_SHIFT: usize = 32;
94macro_rules! to_builtin {
95    ($error:expr) => {
96        ($error as u64) << BUILTIN_BIT_SHIFT
97    };
98}
99
100pub const CUSTOM_ZERO: u64 = to_builtin!(1);
101pub const INVALID_ARGUMENT: u64 = to_builtin!(2);
102pub const INVALID_INSTRUCTION_DATA: u64 = to_builtin!(3);
103pub const INVALID_ACCOUNT_DATA: u64 = to_builtin!(4);
104pub const ACCOUNT_DATA_TOO_SMALL: u64 = to_builtin!(5);
105pub const INSUFFICIENT_FUNDS: u64 = to_builtin!(6);
106pub const INCORRECT_PROGRAM_ID: u64 = to_builtin!(7);
107pub const MISSING_REQUIRED_SIGNATURES: u64 = to_builtin!(8);
108pub const ACCOUNT_ALREADY_INITIALIZED: u64 = to_builtin!(9);
109pub const UNINITIALIZED_ACCOUNT: u64 = to_builtin!(10);
110pub const NOT_ENOUGH_ACCOUNT_KEYS: u64 = to_builtin!(11);
111pub const ACCOUNT_BORROW_FAILED: u64 = to_builtin!(12);
112pub const MAX_SEED_LENGTH_EXCEEDED: u64 = to_builtin!(13);
113pub const INVALID_SEEDS: u64 = to_builtin!(14);
114pub const BORSH_IO_ERROR: u64 = to_builtin!(15);
115pub const ACCOUNT_NOT_RENT_EXEMPT: u64 = to_builtin!(16);
116pub const UNSUPPORTED_SYSVAR: u64 = to_builtin!(17);
117pub const ILLEGAL_OWNER: u64 = to_builtin!(18);
118// Warning: Any new program errors added here must also be:
119// - Added to the below conversions
120// - Added as an equivilent to InstructionError
121// - Be featureized in the BPF loader to return `InstructionError::InvalidError`
122//   until the feature is activated
123
124impl From<ProgramError> for u64 {
125    fn from(error: ProgramError) -> Self {
126        match error {
127            ProgramError::InvalidArgument => INVALID_ARGUMENT,
128            ProgramError::InvalidInstructionData => INVALID_INSTRUCTION_DATA,
129            ProgramError::InvalidAccountData => INVALID_ACCOUNT_DATA,
130            ProgramError::AccountDataTooSmall => ACCOUNT_DATA_TOO_SMALL,
131            ProgramError::InsufficientFunds => INSUFFICIENT_FUNDS,
132            ProgramError::IncorrectProgramId => INCORRECT_PROGRAM_ID,
133            ProgramError::MissingRequiredSignature => MISSING_REQUIRED_SIGNATURES,
134            ProgramError::AccountAlreadyInitialized => ACCOUNT_ALREADY_INITIALIZED,
135            ProgramError::UninitializedAccount => UNINITIALIZED_ACCOUNT,
136            ProgramError::NotEnoughAccountKeys => NOT_ENOUGH_ACCOUNT_KEYS,
137            ProgramError::AccountBorrowFailed => ACCOUNT_BORROW_FAILED,
138            ProgramError::MaxSeedLengthExceeded => MAX_SEED_LENGTH_EXCEEDED,
139            ProgramError::InvalidSeeds => INVALID_SEEDS,
140            ProgramError::BorshIoError(_) => BORSH_IO_ERROR,
141            ProgramError::AccountNotRentExempt => ACCOUNT_NOT_RENT_EXEMPT,
142            ProgramError::UnsupportedSysvar => UNSUPPORTED_SYSVAR,
143            ProgramError::IllegalOwner => ILLEGAL_OWNER,
144            ProgramError::Custom(error) => {
145                if error == 0 {
146                    CUSTOM_ZERO
147                } else {
148                    error as u64
149                }
150            }
151        }
152    }
153}
154
155impl From<u64> for ProgramError {
156    fn from(error: u64) -> Self {
157        match error {
158            CUSTOM_ZERO => Self::Custom(0),
159            INVALID_ARGUMENT => Self::InvalidArgument,
160            INVALID_INSTRUCTION_DATA => Self::InvalidInstructionData,
161            INVALID_ACCOUNT_DATA => Self::InvalidAccountData,
162            ACCOUNT_DATA_TOO_SMALL => Self::AccountDataTooSmall,
163            INSUFFICIENT_FUNDS => Self::InsufficientFunds,
164            INCORRECT_PROGRAM_ID => Self::IncorrectProgramId,
165            MISSING_REQUIRED_SIGNATURES => Self::MissingRequiredSignature,
166            ACCOUNT_ALREADY_INITIALIZED => Self::AccountAlreadyInitialized,
167            UNINITIALIZED_ACCOUNT => Self::UninitializedAccount,
168            NOT_ENOUGH_ACCOUNT_KEYS => Self::NotEnoughAccountKeys,
169            ACCOUNT_BORROW_FAILED => Self::AccountBorrowFailed,
170            MAX_SEED_LENGTH_EXCEEDED => Self::MaxSeedLengthExceeded,
171            INVALID_SEEDS => Self::InvalidSeeds,
172            BORSH_IO_ERROR => Self::BorshIoError("Unkown".to_string()),
173            ACCOUNT_NOT_RENT_EXEMPT => Self::AccountNotRentExempt,
174            UNSUPPORTED_SYSVAR => Self::UnsupportedSysvar,
175            ILLEGAL_OWNER => Self::IllegalOwner,
176            _ => Self::Custom(error as u32),
177        }
178    }
179}
180
181impl TryFrom<InstructionError> for ProgramError {
182    type Error = InstructionError;
183
184    fn try_from(error: InstructionError) -> Result<Self, Self::Error> {
185        match error {
186            Self::Error::Custom(err) => Ok(Self::Custom(err)),
187            Self::Error::InvalidArgument => Ok(Self::InvalidArgument),
188            Self::Error::InvalidInstructionData => Ok(Self::InvalidInstructionData),
189            Self::Error::InvalidAccountData => Ok(Self::InvalidAccountData),
190            Self::Error::AccountDataTooSmall => Ok(Self::AccountDataTooSmall),
191            Self::Error::InsufficientFunds => Ok(Self::InsufficientFunds),
192            Self::Error::IncorrectProgramId => Ok(Self::IncorrectProgramId),
193            Self::Error::MissingRequiredSignature => Ok(Self::MissingRequiredSignature),
194            Self::Error::AccountAlreadyInitialized => Ok(Self::AccountAlreadyInitialized),
195            Self::Error::UninitializedAccount => Ok(Self::UninitializedAccount),
196            Self::Error::NotEnoughAccountKeys => Ok(Self::NotEnoughAccountKeys),
197            Self::Error::AccountBorrowFailed => Ok(Self::AccountBorrowFailed),
198            Self::Error::MaxSeedLengthExceeded => Ok(Self::MaxSeedLengthExceeded),
199            Self::Error::InvalidSeeds => Ok(Self::InvalidSeeds),
200            Self::Error::BorshIoError(err) => Ok(Self::BorshIoError(err)),
201            Self::Error::AccountNotRentExempt => Ok(Self::AccountNotRentExempt),
202            Self::Error::UnsupportedSysvar => Ok(Self::UnsupportedSysvar),
203            Self::Error::IllegalOwner => Ok(Self::IllegalOwner),
204            _ => Err(error),
205        }
206    }
207}
208
209impl<T> From<T> for InstructionError
210where
211    T: ToPrimitive,
212{
213    fn from(error: T) -> Self {
214        let error = error.to_u64().unwrap_or(0xbad_c0de);
215        match error {
216            CUSTOM_ZERO => Self::Custom(0),
217            INVALID_ARGUMENT => Self::InvalidArgument,
218            INVALID_INSTRUCTION_DATA => Self::InvalidInstructionData,
219            INVALID_ACCOUNT_DATA => Self::InvalidAccountData,
220            ACCOUNT_DATA_TOO_SMALL => Self::AccountDataTooSmall,
221            INSUFFICIENT_FUNDS => Self::InsufficientFunds,
222            INCORRECT_PROGRAM_ID => Self::IncorrectProgramId,
223            MISSING_REQUIRED_SIGNATURES => Self::MissingRequiredSignature,
224            ACCOUNT_ALREADY_INITIALIZED => Self::AccountAlreadyInitialized,
225            UNINITIALIZED_ACCOUNT => Self::UninitializedAccount,
226            NOT_ENOUGH_ACCOUNT_KEYS => Self::NotEnoughAccountKeys,
227            ACCOUNT_BORROW_FAILED => Self::AccountBorrowFailed,
228            MAX_SEED_LENGTH_EXCEEDED => Self::MaxSeedLengthExceeded,
229            INVALID_SEEDS => Self::InvalidSeeds,
230            BORSH_IO_ERROR => Self::BorshIoError("Unkown".to_string()),
231            ACCOUNT_NOT_RENT_EXEMPT => Self::AccountNotRentExempt,
232            UNSUPPORTED_SYSVAR => Self::UnsupportedSysvar,
233            ILLEGAL_OWNER => Self::IllegalOwner,
234            _ => {
235                // A valid custom error has no bits set in the upper 32
236                if error >> BUILTIN_BIT_SHIFT == 0 {
237                    Self::Custom(error as u32)
238                } else {
239                    Self::InvalidError
240                }
241            }
242        }
243    }
244}
245
246impl From<PubkeyError> for ProgramError {
247    fn from(error: PubkeyError) -> Self {
248        match error {
249            PubkeyError::MaxSeedLengthExceeded => Self::MaxSeedLengthExceeded,
250            PubkeyError::InvalidSeeds => Self::InvalidSeeds,
251            PubkeyError::IllegalOwner => Self::IllegalOwner,
252        }
253    }
254}
255
256impl From<BorshIoError> for ProgramError {
257    fn from(error: BorshIoError) -> Self {
258        Self::BorshIoError(format!("{}", error))
259    }
260}