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