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