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