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