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