solana_transaction_error/
lib.rs1#![cfg_attr(feature = "frozen-abi", feature(min_specialization))]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3#[cfg(feature = "serde")]
4use serde_derive::{Deserialize, Serialize};
5#[cfg(feature = "frozen-abi")]
6use solana_frozen_abi_macro::{AbiEnumVisitor, AbiExample};
7use {core::fmt, solana_instruction_error::InstructionError, solana_sanitize::SanitizeError};
8
9pub type TransactionResult<T> = Result<T, TransactionError>;
10
11#[cfg_attr(feature = "frozen-abi", derive(AbiExample, AbiEnumVisitor))]
13#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
14#[cfg_attr(feature = "wincode", derive(wincode::SchemaWrite, wincode::SchemaRead))]
15#[derive(Debug, PartialEq, Eq, Clone)]
16pub enum TransactionError {
17 AccountInUse,
20
21 AccountLoadedTwice,
24
25 AccountNotFound,
27
28 ProgramAccountNotFound,
30
31 InsufficientFundsForFee,
33
34 InvalidAccountForFee,
36
37 AlreadyProcessed,
41
42 BlockhashNotFound,
45
46 InstructionError(u8, InstructionError),
49
50 CallChainTooDeep,
52
53 MissingSignatureForFee,
55
56 InvalidAccountIndex,
58
59 SignatureFailure,
61
62 InvalidProgramForExecution,
64
65 SanitizeFailure,
69
70 ClusterMaintenance,
71
72 AccountBorrowOutstanding,
74
75 WouldExceedMaxBlockCostLimit,
77
78 UnsupportedVersion,
80
81 InvalidWritableAccount,
83
84 WouldExceedMaxAccountCostLimit,
86
87 WouldExceedAccountDataBlockLimit,
89
90 TooManyAccountLocks,
92
93 AddressLookupTableNotFound,
95
96 InvalidAddressLookupTableOwner,
98
99 InvalidAddressLookupTableData,
101
102 InvalidAddressLookupTableIndex,
104
105 InvalidRentPayingAccount,
107
108 WouldExceedMaxVoteCostLimit,
110
111 WouldExceedAccountDataTotalLimit,
113
114 DuplicateInstruction(u8),
116
117 InsufficientFundsForRent {
119 account_index: u8,
120 },
121
122 MaxLoadedAccountsDataSizeExceeded,
124
125 InvalidLoadedAccountsDataSizeLimit,
127
128 ResanitizationNeeded,
130
131 ProgramExecutionTemporarilyRestricted {
133 account_index: u8,
134 },
135
136 UnbalancedTransaction,
138
139 ProgramCacheHitMaxLimit,
141
142 CommitCancelled,
144}
145
146impl core::error::Error for TransactionError {}
147
148impl fmt::Display for TransactionError {
149 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
150 match self {
151 Self::AccountInUse
152 => f.write_str("Account in use"),
153 Self::AccountLoadedTwice
154 => f.write_str("Account loaded twice"),
155 Self::AccountNotFound
156 => f.write_str("Attempt to debit an account but found no record of a prior credit."),
157 Self::ProgramAccountNotFound
158 => f.write_str("Attempt to load a program that does not exist"),
159 Self::InsufficientFundsForFee
160 => f.write_str("Insufficient funds for fee"),
161 Self::InvalidAccountForFee
162 => f.write_str("This account may not be used to pay transaction fees"),
163 Self::AlreadyProcessed
164 => f.write_str("This transaction has already been processed"),
165 Self::BlockhashNotFound
166 => f.write_str("Blockhash not found"),
167 Self::InstructionError(idx, err) => write!(f, "Error processing Instruction {idx}: {err}"),
168 Self::CallChainTooDeep
169 => f.write_str("Loader call chain is too deep"),
170 Self::MissingSignatureForFee
171 => f.write_str("Transaction requires a fee but has no signature present"),
172 Self::InvalidAccountIndex
173 => f.write_str("Transaction contains an invalid account reference"),
174 Self::SignatureFailure
175 => f.write_str("Transaction did not pass signature verification"),
176 Self::InvalidProgramForExecution
177 => f.write_str("This program may not be used for executing instructions"),
178 Self::SanitizeFailure
179 => f.write_str("Transaction failed to sanitize accounts offsets correctly"),
180 Self::ClusterMaintenance
181 => f.write_str("Transactions are currently disabled due to cluster maintenance"),
182 Self::AccountBorrowOutstanding
183 => f.write_str("Transaction processing left an account with an outstanding borrowed reference"),
184 Self::WouldExceedMaxBlockCostLimit
185 => f.write_str("Transaction would exceed max Block Cost Limit"),
186 Self::UnsupportedVersion
187 => f.write_str("Transaction version is unsupported"),
188 Self::InvalidWritableAccount
189 => f.write_str("Transaction loads a writable account that cannot be written"),
190 Self::WouldExceedMaxAccountCostLimit
191 => f.write_str("Transaction would exceed max account limit within the block"),
192 Self::WouldExceedAccountDataBlockLimit
193 => f.write_str("Transaction would exceed account data limit within the block"),
194 Self::TooManyAccountLocks
195 => f.write_str("Transaction locked too many accounts"),
196 Self::AddressLookupTableNotFound
197 => f.write_str("Transaction loads an address table account that doesn't exist"),
198 Self::InvalidAddressLookupTableOwner
199 => f.write_str("Transaction loads an address table account with an invalid owner"),
200 Self::InvalidAddressLookupTableData
201 => f.write_str("Transaction loads an address table account with invalid data"),
202 Self::InvalidAddressLookupTableIndex
203 => f.write_str("Transaction address table lookup uses an invalid index"),
204 Self::InvalidRentPayingAccount
205 => f.write_str("Transaction leaves an account with a lower balance than rent-exempt minimum"),
206 Self::WouldExceedMaxVoteCostLimit
207 => f.write_str("Transaction would exceed max Vote Cost Limit"),
208 Self::WouldExceedAccountDataTotalLimit
209 => f.write_str("Transaction would exceed total account data limit"),
210 Self::DuplicateInstruction(idx) => write!(f, "Transaction contains a duplicate instruction ({idx}) that is not allowed"),
211 Self::InsufficientFundsForRent {
212 account_index
213 } => write!(f,"Transaction results in an account ({account_index}) with insufficient funds for rent"),
214 Self::MaxLoadedAccountsDataSizeExceeded
215 => f.write_str("Transaction exceeded max loaded accounts data size cap"),
216 Self::InvalidLoadedAccountsDataSizeLimit
217 => f.write_str("LoadedAccountsDataSizeLimit set for transaction must be greater than 0."),
218 Self::ResanitizationNeeded
219 => f.write_str("ResanitizationNeeded"),
220 Self::ProgramExecutionTemporarilyRestricted {
221 account_index
222 } => write!(f,"Execution of the program referenced by account at index {account_index} is temporarily restricted."),
223 Self::UnbalancedTransaction
224 => f.write_str("Sum of account balances before and after transaction do not match"),
225 Self::ProgramCacheHitMaxLimit
226 => f.write_str("Program cache hit max limit"),
227 Self::CommitCancelled
228 => f.write_str("CommitCancelled"),
229 }
230 }
231}
232
233impl From<SanitizeError> for TransactionError {
234 fn from(_: SanitizeError) -> Self {
235 Self::SanitizeFailure
236 }
237}
238
239#[cfg(not(target_os = "solana"))]
240impl From<SanitizeMessageError> for TransactionError {
241 fn from(err: SanitizeMessageError) -> Self {
242 match err {
243 SanitizeMessageError::AddressLoaderError(err) => Self::from(err),
244 _ => Self::SanitizeFailure,
245 }
246 }
247}
248
249#[cfg(not(target_os = "solana"))]
250#[derive(Debug, PartialEq, Eq, Clone)]
251pub enum AddressLoaderError {
252 Disabled,
254
255 SlotHashesSysvarNotFound,
257
258 LookupTableAccountNotFound,
260
261 InvalidAccountOwner,
263
264 InvalidAccountData,
266
267 InvalidLookupIndex,
269}
270
271#[cfg(not(target_os = "solana"))]
272impl core::error::Error for AddressLoaderError {}
273
274#[cfg(not(target_os = "solana"))]
275impl fmt::Display for AddressLoaderError {
276 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
277 match self {
278 Self::Disabled => f.write_str("Address loading from lookup tables is disabled"),
279 Self::SlotHashesSysvarNotFound => f.write_str("Failed to load slot hashes sysvar"),
280 Self::LookupTableAccountNotFound => {
281 f.write_str("Attempted to lookup addresses from a table that does not exist")
282 }
283 Self::InvalidAccountOwner => f.write_str(
284 "Attempted to lookup addresses from an account owned by the wrong program",
285 ),
286 Self::InvalidAccountData => {
287 f.write_str("Attempted to lookup addresses from an invalid account")
288 }
289 Self::InvalidLookupIndex => f.write_str("Address lookup contains an invalid index"),
290 }
291 }
292}
293
294#[cfg(not(target_os = "solana"))]
295impl From<AddressLoaderError> for TransactionError {
296 fn from(err: AddressLoaderError) -> Self {
297 match err {
298 AddressLoaderError::Disabled => Self::UnsupportedVersion,
299 AddressLoaderError::SlotHashesSysvarNotFound => Self::AccountNotFound,
300 AddressLoaderError::LookupTableAccountNotFound => Self::AddressLookupTableNotFound,
301 AddressLoaderError::InvalidAccountOwner => Self::InvalidAddressLookupTableOwner,
302 AddressLoaderError::InvalidAccountData => Self::InvalidAddressLookupTableData,
303 AddressLoaderError::InvalidLookupIndex => Self::InvalidAddressLookupTableIndex,
304 }
305 }
306}
307
308#[cfg(not(target_os = "solana"))]
309#[derive(PartialEq, Debug, Eq, Clone)]
310pub enum SanitizeMessageError {
311 IndexOutOfBounds,
312 ValueOutOfBounds,
313 InvalidValue,
314 AddressLoaderError(AddressLoaderError),
315}
316
317#[cfg(not(target_os = "solana"))]
318impl core::error::Error for SanitizeMessageError {
319 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
320 match self {
321 Self::IndexOutOfBounds => None,
322 Self::ValueOutOfBounds => None,
323 Self::InvalidValue => None,
324 Self::AddressLoaderError(e) => Some(e),
325 }
326 }
327}
328
329#[cfg(not(target_os = "solana"))]
330impl fmt::Display for SanitizeMessageError {
331 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
332 match self {
333 Self::IndexOutOfBounds => f.write_str("index out of bounds"),
334 Self::ValueOutOfBounds => f.write_str("value out of bounds"),
335 Self::InvalidValue => f.write_str("invalid value"),
336 Self::AddressLoaderError(e) => {
337 write!(f, "{e}")
338 }
339 }
340 }
341}
342#[cfg(not(target_os = "solana"))]
343impl From<AddressLoaderError> for SanitizeMessageError {
344 fn from(source: AddressLoaderError) -> Self {
345 SanitizeMessageError::AddressLoaderError(source)
346 }
347}
348
349#[cfg(not(target_os = "solana"))]
350impl From<SanitizeError> for SanitizeMessageError {
351 fn from(err: SanitizeError) -> Self {
352 match err {
353 SanitizeError::IndexOutOfBounds => Self::IndexOutOfBounds,
354 SanitizeError::ValueOutOfBounds => Self::ValueOutOfBounds,
355 SanitizeError::InvalidValue => Self::InvalidValue,
356 }
357 }
358}
359
360#[cfg(not(target_os = "solana"))]
361#[derive(Debug)]
362pub enum TransportError {
363 IoError(std::io::Error),
364 TransactionError(TransactionError),
365 Custom(String),
366}
367
368#[cfg(not(target_os = "solana"))]
369impl core::error::Error for TransportError {
370 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
371 match self {
372 TransportError::IoError(e) => Some(e),
373 TransportError::TransactionError(e) => Some(e),
374 TransportError::Custom(_) => None,
375 }
376 }
377}
378
379#[cfg(not(target_os = "solana"))]
380impl fmt::Display for TransportError {
381 fn fmt(&self, f: &mut fmt::Formatter) -> ::core::fmt::Result {
382 match self {
383 Self::IoError(e) => f.write_fmt(format_args!("transport io error: {e}")),
384 Self::TransactionError(e) => {
385 f.write_fmt(format_args!("transport transaction error: {e}"))
386 }
387 Self::Custom(s) => f.write_fmt(format_args!("transport custom error: {s}")),
388 }
389 }
390}
391
392#[cfg(not(target_os = "solana"))]
393impl From<std::io::Error> for TransportError {
394 fn from(e: std::io::Error) -> Self {
395 TransportError::IoError(e)
396 }
397}
398
399#[cfg(not(target_os = "solana"))]
400impl From<TransactionError> for TransportError {
401 fn from(e: TransactionError) -> Self {
402 TransportError::TransactionError(e)
403 }
404}
405
406#[cfg(not(target_os = "solana"))]
407impl TransportError {
408 pub fn unwrap(&self) -> TransactionError {
409 if let TransportError::TransactionError(err) = self {
410 err.clone()
411 } else {
412 panic!("unexpected transport error")
413 }
414 }
415}
416
417#[cfg(not(target_os = "solana"))]
418pub type TransportResult<T> = std::result::Result<T, TransportError>;