solana_sdk/transaction/
error.rs

1use {
2    crate::{
3        instruction::InstructionError,
4        message::{AddressLoaderError, SanitizeMessageError},
5        sanitize::SanitizeError,
6    },
7    serde::Serialize,
8    thiserror::Error,
9};
10
11/// Reasons a transaction might be rejected.
12#[derive(
13    Error, Serialize, Deserialize, Debug, PartialEq, Eq, Clone, AbiExample, AbiEnumVisitor,
14)]
15pub enum TransactionError {
16    /// An account is already being processed in another transaction in a way
17    /// that does not support parallelism
18    #[error("Account in use")]
19    AccountInUse,
20
21    /// A `Pubkey` appears twice in the transaction's `account_keys`.  Instructions can reference
22    /// `Pubkey`s more than once but the message must contain a list with no duplicate keys
23    #[error("Account loaded twice")]
24    AccountLoadedTwice,
25
26    /// Attempt to debit an account but found no record of a prior credit.
27    #[error("Attempt to debit an account but found no record of a prior credit.")]
28    AccountNotFound,
29
30    /// Attempt to load a program that does not exist
31    #[error("Attempt to load a program that does not exist")]
32    ProgramAccountNotFound,
33
34    /// The from `Pubkey` does not have sufficient balance to pay the fee to schedule the transaction
35    #[error("Insufficient funds for fee")]
36    InsufficientFundsForFee,
37
38    /// This account may not be used to pay transaction fees
39    #[error("This account may not be used to pay transaction fees")]
40    InvalidAccountForFee,
41
42    /// The bank has seen this transaction before. This can occur under normal operation
43    /// when a UDP packet is duplicated, as a user error from a client not updating
44    /// its `recent_blockhash`, or as a double-spend attack.
45    #[error("This transaction has already been processed")]
46    AlreadyProcessed,
47
48    /// The bank has not seen the given `recent_blockhash` or the transaction is too old and
49    /// the `recent_blockhash` has been discarded.
50    #[error("Blockhash not found")]
51    BlockhashNotFound,
52
53    /// An error occurred while processing an instruction. The first element of the tuple
54    /// indicates the instruction index in which the error occurred.
55    #[error("Error processing Instruction {0}: {1}")]
56    InstructionError(u8, InstructionError),
57
58    /// Loader call chain is too deep
59    #[error("Loader call chain is too deep")]
60    CallChainTooDeep,
61
62    /// Transaction requires a fee but has no signature present
63    #[error("Transaction requires a fee but has no signature present")]
64    MissingSignatureForFee,
65
66    /// Transaction contains an invalid account reference
67    #[error("Transaction contains an invalid account reference")]
68    InvalidAccountIndex,
69
70    /// Transaction did not pass signature verification
71    #[error("Transaction did not pass signature verification")]
72    SignatureFailure,
73
74    /// This program may not be used for executing instructions
75    #[error("This program may not be used for executing instructions")]
76    InvalidProgramForExecution,
77
78    /// Transaction failed to sanitize accounts offsets correctly
79    /// implies that account locks are not taken for this TX, and should
80    /// not be unlocked.
81    #[error("Transaction failed to sanitize accounts offsets correctly")]
82    SanitizeFailure,
83
84    #[error("Transactions are currently disabled due to cluster maintenance")]
85    ClusterMaintenance,
86
87    /// Transaction processing left an account with an outstanding borrowed reference
88    #[error("Transaction processing left an account with an outstanding borrowed reference")]
89    AccountBorrowOutstanding,
90
91    /// Transaction would exceed max Block Cost Limit
92    #[error("Transaction would exceed max Block Cost Limit")]
93    WouldExceedMaxBlockCostLimit,
94
95    /// Transaction version is unsupported
96    #[error("Transaction version is unsupported")]
97    UnsupportedVersion,
98
99    /// Transaction loads a writable account that cannot be written
100    #[error("Transaction loads a writable account that cannot be written")]
101    InvalidWritableAccount,
102
103    /// Transaction would exceed max account limit within the block
104    #[error("Transaction would exceed max account limit within the block")]
105    WouldExceedMaxAccountCostLimit,
106
107    /// Transaction would exceed account data limit within the block
108    #[error("Transaction would exceed account data limit within the block")]
109    WouldExceedAccountDataBlockLimit,
110
111    /// Transaction locked too many accounts
112    #[error("Transaction locked too many accounts")]
113    TooManyAccountLocks,
114
115    /// Address lookup table not found
116    #[error("Transaction loads an address table account that doesn't exist")]
117    AddressLookupTableNotFound,
118
119    /// Attempted to lookup addresses from an account owned by the wrong program
120    #[error("Transaction loads an address table account with an invalid owner")]
121    InvalidAddressLookupTableOwner,
122
123    /// Attempted to lookup addresses from an invalid account
124    #[error("Transaction loads an address table account with invalid data")]
125    InvalidAddressLookupTableData,
126
127    /// Address table lookup uses an invalid index
128    #[error("Transaction address table lookup uses an invalid index")]
129    InvalidAddressLookupTableIndex,
130
131    /// Transaction leaves an account with a lower balance than rent-exempt minimum
132    #[error("Transaction leaves an account with a lower balance than rent-exempt minimum")]
133    InvalidRentPayingAccount,
134
135    /// Transaction would exceed max Vote Cost Limit
136    #[error("Transaction would exceed max Vote Cost Limit")]
137    WouldExceedMaxVoteCostLimit,
138
139    /// Transaction would exceed total account data limit
140    #[error("Transaction would exceed total account data limit")]
141    WouldExceedAccountDataTotalLimit,
142
143    /// Transaction contains a duplicate instruction that is not allowed
144    #[error("Transaction contains a duplicate instruction ({0}) that is not allowed")]
145    DuplicateInstruction(u8),
146
147    /// Transaction results in an account without insufficient funds for rent
148    #[error(
149        "Transaction results in an account ({account_index}) without insufficient funds for rent"
150    )]
151    InsufficientFundsForRent { account_index: u8 },
152}
153
154impl From<SanitizeError> for TransactionError {
155    fn from(_: SanitizeError) -> Self {
156        Self::SanitizeFailure
157    }
158}
159
160impl From<SanitizeMessageError> for TransactionError {
161    fn from(err: SanitizeMessageError) -> Self {
162        match err {
163            SanitizeMessageError::AddressLoaderError(err) => Self::from(err),
164            _ => Self::SanitizeFailure,
165        }
166    }
167}
168
169impl From<AddressLoaderError> for TransactionError {
170    fn from(err: AddressLoaderError) -> Self {
171        match err {
172            AddressLoaderError::Disabled => Self::UnsupportedVersion,
173            AddressLoaderError::SlotHashesSysvarNotFound => Self::AccountNotFound,
174            AddressLoaderError::LookupTableAccountNotFound => Self::AddressLookupTableNotFound,
175            AddressLoaderError::InvalidAccountOwner => Self::InvalidAddressLookupTableOwner,
176            AddressLoaderError::InvalidAccountData => Self::InvalidAddressLookupTableData,
177            AddressLoaderError::InvalidLookupIndex => Self::InvalidAddressLookupTableIndex,
178        }
179    }
180}