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