From 31bcb948464560e473be7897d0611c2c751c2a5d Mon Sep 17 00:00:00 2001 From: Kevin Heavey Date: Tue, 22 Oct 2024 16:42:09 +0400 Subject: [PATCH] extract transaction-error crate (#2295) * extract transaction-error crate * update TransactionError usage in sdk * fix frozen-abi support * make serde optional in solana-transaction-error * update lock file * fmt * add #[cfg(not(target_os = "solana"))] where applicable * fmt * put solana-transaction-error behind feature "full" * use workspace lints * update digest * make rustc_version optional * avoid frozen-abi build script * add back workspace lints * remove thiserror * remove unused build deps * use solana-instruction directly * move AddressLoaderError and SanitizeMessageError to transaction-error, and fix its solana-instruction dep features * lint * move TransportError to transaction-error crate * use doc_auto_cfg like in #3121 * missing comma * add deprecations to re-exports --- Cargo.lock | 14 + Cargo.toml | 2 + programs/sbf/Cargo.lock | 12 + runtime/src/bank.rs | 2 +- sdk/Cargo.toml | 7 +- sdk/program/Cargo.toml | 1 + sdk/program/src/message/address_loader.rs | 37 +- sdk/program/src/message/sanitized.rs | 33 +- sdk/src/signer/mod.rs | 2 +- sdk/src/transaction/error.rs | 203 ----------- sdk/src/transaction/mod.rs | 5 +- sdk/src/transaction/sanitized.rs | 3 +- sdk/src/transaction/versioned/mod.rs | 3 +- sdk/src/transport.rs | 26 +- sdk/transaction-error/Cargo.toml | 32 ++ sdk/transaction-error/src/lib.rs | 412 ++++++++++++++++++++++ 16 files changed, 502 insertions(+), 292 deletions(-) delete mode 100644 sdk/src/transaction/error.rs create mode 100644 sdk/transaction-error/Cargo.toml create mode 100644 sdk/transaction-error/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 67e666a08aab40..df8909486cc620 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7380,6 +7380,7 @@ dependencies = [ "solana-short-vec", "solana-slot-hashes", "solana-stable-layout", + "solana-transaction-error", "static_assertions", "test-case", "thiserror", @@ -7969,6 +7970,7 @@ dependencies = [ "solana-serde-varint", "solana-short-vec", "solana-signature", + "solana-transaction-error", "static_assertions", "thiserror", "tiny-bip39", @@ -8545,6 +8547,18 @@ dependencies = [ "solana-version", ] +[[package]] +name = "solana-transaction-error" +version = "2.1.0" +dependencies = [ + "serde", + "serde_derive", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-instruction", + "solana-sanitize", +] + [[package]] name = "solana-transaction-metrics-tracker" version = "2.1.0" diff --git a/Cargo.toml b/Cargo.toml index 9bddfcac6d1287..116d51afdd842b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -137,6 +137,7 @@ members = [ "sdk/signature", "sdk/slot-hashes", "sdk/stable-layout", + "sdk/transaction-error", "send-transaction-service", "short-vec", "stake-accounts", @@ -497,6 +498,7 @@ solana-svm-transaction = { path = "svm-transaction", version = "=2.1.0" } solana-system-program = { path = "programs/system", version = "=2.1.0" } solana-test-validator = { path = "test-validator", version = "=2.1.0" } solana-thin-client = { path = "thin-client", version = "=2.1.0" } +solana-transaction-error = { path = "sdk/transaction-error", version = "=2.1.0" } solana-tpu-client = { path = "tpu-client", version = "=2.1.0", default-features = false } solana-transaction-status = { path = "transaction-status", version = "=2.1.0" } solana-transaction-status-client-types = { path = "transaction-status-client-types", version = "=2.1.0" } diff --git a/programs/sbf/Cargo.lock b/programs/sbf/Cargo.lock index 95b68c7791496b..2567d497e61c0a 100644 --- a/programs/sbf/Cargo.lock +++ b/programs/sbf/Cargo.lock @@ -5756,6 +5756,7 @@ dependencies = [ "solana-short-vec", "solana-slot-hashes", "solana-stable-layout", + "solana-transaction-error", "thiserror", "wasm-bindgen", ] @@ -6721,6 +6722,7 @@ dependencies = [ "solana-serde-varint", "solana-short-vec", "solana-signature", + "solana-transaction-error", "thiserror", "wasm-bindgen", ] @@ -7057,6 +7059,16 @@ dependencies = [ "tokio", ] +[[package]] +name = "solana-transaction-error" +version = "2.1.0" +dependencies = [ + "serde", + "serde_derive", + "solana-instruction", + "solana-sanitize", +] + [[package]] name = "solana-transaction-metrics-tracker" version = "2.1.0" diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index e3cdae242f9c90..d88c03b0662a60 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -253,7 +253,7 @@ struct RentMetrics { pub type BankStatusCache = StatusCache>; #[cfg_attr( feature = "frozen-abi", - frozen_abi(digest = "BswQL6n7kKwgHFKcwMCQcrWjt8h59Vh6KkNb75iaqG2B") + frozen_abi(digest = "BHg4qpwegtaJypLUqAdjQYzYeLfEGf6tA4U5cREbHMHi") )] pub type BankSlotDelta = SlotDelta>; diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index 4872798563020d..0c8352fc02661a 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -34,7 +34,8 @@ full = [ "sha3", "digest", "solana-pubkey/rand", - "dep:solana-precompile-error" + "dep:solana-precompile-error", + "dep:solana-transaction-error" ] borsh = ["dep:borsh", "solana-program/borsh", "solana-secp256k1-recover/borsh"] dev-context-only-utils = ["qualifier_attr", "solana-account/dev-context-only-utils"] @@ -45,7 +46,8 @@ frozen-abi = [ "solana-account/frozen-abi", "solana-program/frozen-abi", "solana-short-vec/frozen-abi", - "solana-signature/frozen-abi" + "solana-signature/frozen-abi", + "solana-transaction-error/frozen-abi" ] [dependencies] @@ -114,6 +116,7 @@ solana-signature = { workspace = true, features = [ "std", "verify", ], optional = true } +solana-transaction-error = { workspace = true, features = ["serde"], optional = true } thiserror = { workspace = true } [target.'cfg(target_arch = "wasm32")'.dependencies] diff --git a/sdk/program/Cargo.toml b/sdk/program/Cargo.toml index 8593cab63f6ed2..6f04f84a7693d8 100644 --- a/sdk/program/Cargo.toml +++ b/sdk/program/Cargo.toml @@ -87,6 +87,7 @@ bitflags = { workspace = true } curve25519-dalek = { workspace = true } num-bigint = { workspace = true } rand = { workspace = true } +solana-transaction-error = { workspace = true } [target.'cfg(not(target_os = "solana"))'.dev-dependencies] arbitrary = { workspace = true, features = ["derive"] } diff --git a/sdk/program/src/message/address_loader.rs b/sdk/program/src/message/address_loader.rs index 83e514cd0bd63b..632117718660bc 100644 --- a/sdk/program/src/message/address_loader.rs +++ b/sdk/program/src/message/address_loader.rs @@ -1,34 +1,9 @@ -use { - super::v0::{LoadedAddresses, MessageAddressTableLookup}, - thiserror::Error, -}; - -#[derive(Debug, Error, PartialEq, Eq, Clone)] -pub enum AddressLoaderError { - /// Address loading from lookup tables is disabled - #[error("Address loading from lookup tables is disabled")] - Disabled, - - /// Failed to load slot hashes sysvar - #[error("Failed to load slot hashes sysvar")] - SlotHashesSysvarNotFound, - - /// Attempted to lookup addresses from a table that does not exist - #[error("Attempted to lookup addresses from a table that does not exist")] - LookupTableAccountNotFound, - - /// Attempted to lookup addresses from an account owned by the wrong program - #[error("Attempted to lookup addresses from an account owned by the wrong program")] - InvalidAccountOwner, - - /// Attempted to lookup addresses from an invalid account - #[error("Attempted to lookup addresses from an invalid account")] - InvalidAccountData, - - /// Address lookup contains an invalid index - #[error("Address lookup contains an invalid index")] - InvalidLookupIndex, -} +use super::v0::{LoadedAddresses, MessageAddressTableLookup}; +#[deprecated( + since = "2.1.0", + note = "Use solana_transaction_error::AddressLoaderError instead" +)] +pub use solana_transaction_error::AddressLoaderError; pub trait AddressLoader: Clone { fn load_addresses( diff --git a/sdk/program/src/message/sanitized.rs b/sdk/program/src/message/sanitized.rs index 3767ffef5bb51f..792dd1c39104ed 100644 --- a/sdk/program/src/message/sanitized.rs +++ b/sdk/program/src/message/sanitized.rs @@ -1,3 +1,8 @@ +#[deprecated( + since = "2.1.0", + note = "Use solana_transaction_error::SanitizeMessageError instead" +)] +pub use solana_transaction_error::SanitizeMessageError; use { crate::{ ed25519_program, @@ -6,8 +11,7 @@ use { message::{ legacy, v0::{self, LoadedAddresses}, - AccountKeys, AddressLoader, AddressLoaderError, MessageHeader, - SanitizedVersionedMessage, VersionedMessage, + AccountKeys, AddressLoader, MessageHeader, SanitizedVersionedMessage, VersionedMessage, }, nonce::NONCED_TX_MARKER_IX_INDEX, program_utils::limited_deserialize, @@ -16,9 +20,8 @@ use { solana_program::{system_instruction::SystemInstruction, system_program}, sysvar::instructions::{BorrowedAccountMeta, BorrowedInstruction}, }, - solana_sanitize::{Sanitize, SanitizeError}, + solana_sanitize::Sanitize, std::{borrow::Cow, collections::HashSet, convert::TryFrom}, - thiserror::Error, }; #[derive(Debug, Clone, Eq, PartialEq)] @@ -80,28 +83,6 @@ pub enum SanitizedMessage { V0(v0::LoadedMessage<'static>), } -#[derive(PartialEq, Debug, Error, Eq, Clone)] -pub enum SanitizeMessageError { - #[error("index out of bounds")] - IndexOutOfBounds, - #[error("value out of bounds")] - ValueOutOfBounds, - #[error("invalid value")] - InvalidValue, - #[error("{0}")] - AddressLoaderError(#[from] AddressLoaderError), -} - -impl From for SanitizeMessageError { - fn from(err: SanitizeError) -> Self { - match err { - SanitizeError::IndexOutOfBounds => Self::IndexOutOfBounds, - SanitizeError::ValueOutOfBounds => Self::ValueOutOfBounds, - SanitizeError::InvalidValue => Self::InvalidValue, - } - } -} - impl SanitizedMessage { /// Create a sanitized message from a sanitized versioned message. /// If the input message uses address tables, attempt to look up the diff --git a/sdk/src/signer/mod.rs b/sdk/src/signer/mod.rs index b5be748ecfb2a3..ad3ca085cbc52b 100644 --- a/sdk/src/signer/mod.rs +++ b/sdk/src/signer/mod.rs @@ -6,10 +6,10 @@ use { crate::{ pubkey::Pubkey, signature::{PresignerError, Signature}, - transaction::TransactionError, }, itertools::Itertools, solana_derivation_path::DerivationPath, + solana_transaction_error::TransactionError, std::{ error, fs::{self, File, OpenOptions}, diff --git a/sdk/src/transaction/error.rs b/sdk/src/transaction/error.rs deleted file mode 100644 index 081cd570eaff84..00000000000000 --- a/sdk/src/transaction/error.rs +++ /dev/null @@ -1,203 +0,0 @@ -use { - crate::{ - instruction::InstructionError, - message::{AddressLoaderError, SanitizeMessageError}, - }, - serde::Serialize, - solana_sanitize::SanitizeError, - thiserror::Error, -}; - -/// Reasons a transaction might be rejected. -#[cfg_attr(feature = "frozen-abi", derive(AbiExample, AbiEnumVisitor))] -#[derive(Error, Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] -pub enum TransactionError { - /// An account is already being processed in another transaction in a way - /// that does not support parallelism - #[error("Account in use")] - AccountInUse, - - /// A `Pubkey` appears twice in the transaction's `account_keys`. Instructions can reference - /// `Pubkey`s more than once but the message must contain a list with no duplicate keys - #[error("Account loaded twice")] - AccountLoadedTwice, - - /// Attempt to debit an account but found no record of a prior credit. - #[error("Attempt to debit an account but found no record of a prior credit.")] - AccountNotFound, - - /// Attempt to load a program that does not exist - #[error("Attempt to load a program that does not exist")] - ProgramAccountNotFound, - - /// The from `Pubkey` does not have sufficient balance to pay the fee to schedule the transaction - #[error("Insufficient funds for fee")] - InsufficientFundsForFee, - - /// This account may not be used to pay transaction fees - #[error("This account may not be used to pay transaction fees")] - InvalidAccountForFee, - - /// The bank has seen this transaction before. This can occur under normal operation - /// when a UDP packet is duplicated, as a user error from a client not updating - /// its `recent_blockhash`, or as a double-spend attack. - #[error("This transaction has already been processed")] - AlreadyProcessed, - - /// The bank has not seen the given `recent_blockhash` or the transaction is too old and - /// the `recent_blockhash` has been discarded. - #[error("Blockhash not found")] - BlockhashNotFound, - - /// An error occurred while processing an instruction. The first element of the tuple - /// indicates the instruction index in which the error occurred. - #[error("Error processing Instruction {0}: {1}")] - InstructionError(u8, InstructionError), - - /// Loader call chain is too deep - #[error("Loader call chain is too deep")] - CallChainTooDeep, - - /// Transaction requires a fee but has no signature present - #[error("Transaction requires a fee but has no signature present")] - MissingSignatureForFee, - - /// Transaction contains an invalid account reference - #[error("Transaction contains an invalid account reference")] - InvalidAccountIndex, - - /// Transaction did not pass signature verification - #[error("Transaction did not pass signature verification")] - SignatureFailure, - - /// This program may not be used for executing instructions - #[error("This program may not be used for executing instructions")] - InvalidProgramForExecution, - - /// Transaction failed to sanitize accounts offsets correctly - /// implies that account locks are not taken for this TX, and should - /// not be unlocked. - #[error("Transaction failed to sanitize accounts offsets correctly")] - SanitizeFailure, - - #[error("Transactions are currently disabled due to cluster maintenance")] - ClusterMaintenance, - - /// Transaction processing left an account with an outstanding borrowed reference - #[error("Transaction processing left an account with an outstanding borrowed reference")] - AccountBorrowOutstanding, - - /// Transaction would exceed max Block Cost Limit - #[error("Transaction would exceed max Block Cost Limit")] - WouldExceedMaxBlockCostLimit, - - /// Transaction version is unsupported - #[error("Transaction version is unsupported")] - UnsupportedVersion, - - /// Transaction loads a writable account that cannot be written - #[error("Transaction loads a writable account that cannot be written")] - InvalidWritableAccount, - - /// Transaction would exceed max account limit within the block - #[error("Transaction would exceed max account limit within the block")] - WouldExceedMaxAccountCostLimit, - - /// Transaction would exceed account data limit within the block - #[error("Transaction would exceed account data limit within the block")] - WouldExceedAccountDataBlockLimit, - - /// Transaction locked too many accounts - #[error("Transaction locked too many accounts")] - TooManyAccountLocks, - - /// Address lookup table not found - #[error("Transaction loads an address table account that doesn't exist")] - AddressLookupTableNotFound, - - /// Attempted to lookup addresses from an account owned by the wrong program - #[error("Transaction loads an address table account with an invalid owner")] - InvalidAddressLookupTableOwner, - - /// Attempted to lookup addresses from an invalid account - #[error("Transaction loads an address table account with invalid data")] - InvalidAddressLookupTableData, - - /// Address table lookup uses an invalid index - #[error("Transaction address table lookup uses an invalid index")] - InvalidAddressLookupTableIndex, - - /// Transaction leaves an account with a lower balance than rent-exempt minimum - #[error("Transaction leaves an account with a lower balance than rent-exempt minimum")] - InvalidRentPayingAccount, - - /// Transaction would exceed max Vote Cost Limit - #[error("Transaction would exceed max Vote Cost Limit")] - WouldExceedMaxVoteCostLimit, - - /// Transaction would exceed total account data limit - #[error("Transaction would exceed total account data limit")] - WouldExceedAccountDataTotalLimit, - - /// Transaction contains a duplicate instruction that is not allowed - #[error("Transaction contains a duplicate instruction ({0}) that is not allowed")] - DuplicateInstruction(u8), - - /// Transaction results in an account with insufficient funds for rent - #[error( - "Transaction results in an account ({account_index}) with insufficient funds for rent" - )] - InsufficientFundsForRent { account_index: u8 }, - - /// Transaction exceeded max loaded accounts data size cap - #[error("Transaction exceeded max loaded accounts data size cap")] - MaxLoadedAccountsDataSizeExceeded, - - /// LoadedAccountsDataSizeLimit set for transaction must be greater than 0. - #[error("LoadedAccountsDataSizeLimit set for transaction must be greater than 0.")] - InvalidLoadedAccountsDataSizeLimit, - - /// Sanitized transaction differed before/after feature activiation. Needs to be resanitized. - #[error("ResanitizationNeeded")] - ResanitizationNeeded, - - /// Program execution is temporarily restricted on an account. - #[error("Execution of the program referenced by account at index {account_index} is temporarily restricted.")] - ProgramExecutionTemporarilyRestricted { account_index: u8 }, - - /// The total balance before the transaction does not equal the total balance after the transaction - #[error("Sum of account balances before and after transaction do not match")] - UnbalancedTransaction, - - /// Program cache hit max limit. - #[error("Program cache hit max limit")] - ProgramCacheHitMaxLimit, -} - -impl From for TransactionError { - fn from(_: SanitizeError) -> Self { - Self::SanitizeFailure - } -} - -impl From for TransactionError { - fn from(err: SanitizeMessageError) -> Self { - match err { - SanitizeMessageError::AddressLoaderError(err) => Self::from(err), - _ => Self::SanitizeFailure, - } - } -} - -impl From for TransactionError { - fn from(err: AddressLoaderError) -> Self { - match err { - AddressLoaderError::Disabled => Self::UnsupportedVersion, - AddressLoaderError::SlotHashesSysvarNotFound => Self::AccountNotFound, - AddressLoaderError::LookupTableAccountNotFound => Self::AddressLookupTableNotFound, - AddressLoaderError::InvalidAccountOwner => Self::InvalidAddressLookupTableOwner, - AddressLoaderError::InvalidAccountData => Self::InvalidAddressLookupTableData, - AddressLoaderError::InvalidLookupIndex => Self::InvalidAddressLookupTableIndex, - } - } -} diff --git a/sdk/src/transaction/mod.rs b/sdk/src/transaction/mod.rs index 18a8d1022708d6..f0fdfce2346d45 100644 --- a/sdk/src/transaction/mod.rs +++ b/sdk/src/transaction/mod.rs @@ -133,11 +133,12 @@ use { std::result, }; -mod error; mod sanitized; mod versioned; -pub use {error::*, sanitized::*, versioned::*}; +#[deprecated(since = "2.1.0", note = "Use solana_transaction_error crate instead")] +pub use solana_transaction_error::*; +pub use {sanitized::*, versioned::*}; #[derive(PartialEq, Eq, Clone, Copy, Debug)] pub enum TransactionVerificationMode { diff --git a/sdk/src/transaction/sanitized.rs b/sdk/src/transaction/sanitized.rs index 10f7c6f048a531..f1ad5915d4e83f 100644 --- a/sdk/src/transaction/sanitized.rs +++ b/sdk/src/transaction/sanitized.rs @@ -15,11 +15,12 @@ use { reserved_account_keys::ReservedAccountKeys, signature::Signature, simple_vote_transaction_checker::is_simple_vote_transaction, - transaction::{Result, Transaction, TransactionError, VersionedTransaction}, + transaction::{Result, Transaction, VersionedTransaction}, }, solana_feature_set as feature_set, solana_program::{instruction::InstructionError, message::SanitizedVersionedMessage}, solana_sanitize::Sanitize, + solana_transaction_error::TransactionError, std::collections::HashSet, }; diff --git a/sdk/src/transaction/versioned/mod.rs b/sdk/src/transaction/versioned/mod.rs index 1305b6ab514587..21bd1cce63b9d2 100644 --- a/sdk/src/transaction/versioned/mod.rs +++ b/sdk/src/transaction/versioned/mod.rs @@ -9,11 +9,12 @@ use { signature::Signature, signer::SignerError, signers::Signers, - transaction::{Result, Transaction, TransactionError}, + transaction::{Result, Transaction}, }, serde::Serialize, solana_sanitize::SanitizeError, solana_short_vec as short_vec, + solana_transaction_error::TransactionError, std::cmp::Ordering, }; diff --git a/sdk/src/transport.rs b/sdk/src/transport.rs index a9ec06c3fe1daa..6ca2b648ccd3dc 100644 --- a/sdk/src/transport.rs +++ b/sdk/src/transport.rs @@ -1,27 +1,5 @@ //! Defines the [`TransportError`] type. #![cfg(feature = "full")] - -use {crate::transaction::TransactionError, std::io, thiserror::Error}; - -#[derive(Debug, Error)] -pub enum TransportError { - #[error("transport io error: {0}")] - IoError(#[from] io::Error), - #[error("transport transaction error: {0}")] - TransactionError(#[from] TransactionError), - #[error("transport custom error: {0}")] - Custom(String), -} - -impl TransportError { - pub fn unwrap(&self) -> TransactionError { - if let TransportError::TransactionError(err) = self { - err.clone() - } else { - panic!("unexpected transport error") - } - } -} - -pub type Result = std::result::Result; +#[deprecated(since = "2.1.0", note = "Use solana_transaction_error crate instead")] +pub use solana_transaction_error::{TransportError, TransportResult as Result}; diff --git a/sdk/transaction-error/Cargo.toml b/sdk/transaction-error/Cargo.toml new file mode 100644 index 00000000000000..33c24903c1666b --- /dev/null +++ b/sdk/transaction-error/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "solana-transaction-error" +description = "Solana TransactionError type" +documentation = "https://docs.rs/solana-transaction-error" +version = { workspace = true } +authors = { workspace = true } +repository = { workspace = true } +homepage = { workspace = true } +license = { workspace = true } +edition = { workspace = true } + +[dependencies] +serde = { workspace = true, optional = true } +serde_derive = { workspace = true, optional = true } +solana-frozen-abi = { workspace = true, optional = true } +solana-frozen-abi-macro = { workspace = true, optional = true } +solana-instruction = { workspace = true, default-features = false, features = [ + "std", +] } +solana-sanitize = { workspace = true } + +[features] +frozen-abi = ["dep:solana-frozen-abi", "dep:solana-frozen-abi-macro"] +serde = ["dep:serde", "dep:serde_derive"] + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] +all-features = true +rustdoc-args = ["--cfg=docsrs"] + +[lints] +workspace = true diff --git a/sdk/transaction-error/src/lib.rs b/sdk/transaction-error/src/lib.rs new file mode 100644 index 00000000000000..de90f8838e401c --- /dev/null +++ b/sdk/transaction-error/src/lib.rs @@ -0,0 +1,412 @@ +#![cfg_attr(feature = "frozen-abi", feature(min_specialization))] +#![cfg_attr(docsrs, feature(doc_auto_cfg))] +#[cfg(feature = "serde")] +use serde_derive::{Deserialize, Serialize}; +#[cfg(feature = "frozen-abi")] +use solana_frozen_abi_macro::{AbiEnumVisitor, AbiExample}; +use { + core::fmt, solana_instruction::error::InstructionError, solana_sanitize::SanitizeError, std::io, +}; + +/// Reasons a transaction might be rejected. +#[cfg_attr(feature = "frozen-abi", derive(AbiExample, AbiEnumVisitor))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum TransactionError { + /// An account is already being processed in another transaction in a way + /// that does not support parallelism + AccountInUse, + + /// A `Pubkey` appears twice in the transaction's `account_keys`. Instructions can reference + /// `Pubkey`s more than once but the message must contain a list with no duplicate keys + AccountLoadedTwice, + + /// Attempt to debit an account but found no record of a prior credit. + AccountNotFound, + + /// Attempt to load a program that does not exist + ProgramAccountNotFound, + + /// The from `Pubkey` does not have sufficient balance to pay the fee to schedule the transaction + InsufficientFundsForFee, + + /// This account may not be used to pay transaction fees + InvalidAccountForFee, + + /// The bank has seen this transaction before. This can occur under normal operation + /// when a UDP packet is duplicated, as a user error from a client not updating + /// its `recent_blockhash`, or as a double-spend attack. + AlreadyProcessed, + + /// The bank has not seen the given `recent_blockhash` or the transaction is too old and + /// the `recent_blockhash` has been discarded. + BlockhashNotFound, + + /// An error occurred while processing an instruction. The first element of the tuple + /// indicates the instruction index in which the error occurred. + InstructionError(u8, InstructionError), + + /// Loader call chain is too deep + CallChainTooDeep, + + /// Transaction requires a fee but has no signature present + MissingSignatureForFee, + + /// Transaction contains an invalid account reference + InvalidAccountIndex, + + /// Transaction did not pass signature verification + SignatureFailure, + + /// This program may not be used for executing instructions + InvalidProgramForExecution, + + /// Transaction failed to sanitize accounts offsets correctly + /// implies that account locks are not taken for this TX, and should + /// not be unlocked. + SanitizeFailure, + + ClusterMaintenance, + + /// Transaction processing left an account with an outstanding borrowed reference + AccountBorrowOutstanding, + + /// Transaction would exceed max Block Cost Limit + WouldExceedMaxBlockCostLimit, + + /// Transaction version is unsupported + UnsupportedVersion, + + /// Transaction loads a writable account that cannot be written + InvalidWritableAccount, + + /// Transaction would exceed max account limit within the block + WouldExceedMaxAccountCostLimit, + + /// Transaction would exceed account data limit within the block + WouldExceedAccountDataBlockLimit, + + /// Transaction locked too many accounts + TooManyAccountLocks, + + /// Address lookup table not found + AddressLookupTableNotFound, + + /// Attempted to lookup addresses from an account owned by the wrong program + InvalidAddressLookupTableOwner, + + /// Attempted to lookup addresses from an invalid account + InvalidAddressLookupTableData, + + /// Address table lookup uses an invalid index + InvalidAddressLookupTableIndex, + + /// Transaction leaves an account with a lower balance than rent-exempt minimum + InvalidRentPayingAccount, + + /// Transaction would exceed max Vote Cost Limit + WouldExceedMaxVoteCostLimit, + + /// Transaction would exceed total account data limit + WouldExceedAccountDataTotalLimit, + + /// Transaction contains a duplicate instruction that is not allowed + DuplicateInstruction(u8), + + /// Transaction results in an account with insufficient funds for rent + InsufficientFundsForRent { + account_index: u8, + }, + + /// Transaction exceeded max loaded accounts data size cap + MaxLoadedAccountsDataSizeExceeded, + + /// LoadedAccountsDataSizeLimit set for transaction must be greater than 0. + InvalidLoadedAccountsDataSizeLimit, + + /// Sanitized transaction differed before/after feature activiation. Needs to be resanitized. + ResanitizationNeeded, + + /// Program execution is temporarily restricted on an account. + ProgramExecutionTemporarilyRestricted { + account_index: u8, + }, + + /// The total balance before the transaction does not equal the total balance after the transaction + UnbalancedTransaction, + + /// Program cache hit max limit. + ProgramCacheHitMaxLimit, +} + +impl std::error::Error for TransactionError {} + +impl fmt::Display for TransactionError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::AccountInUse + => f.write_str("Account in use"), + Self::AccountLoadedTwice + => f.write_str("Account loaded twice"), + Self::AccountNotFound + => f.write_str("Attempt to debit an account but found no record of a prior credit."), + Self::ProgramAccountNotFound + => f.write_str("Attempt to load a program that does not exist"), + Self::InsufficientFundsForFee + => f.write_str("Insufficient funds for fee"), + Self::InvalidAccountForFee + => f.write_str("This account may not be used to pay transaction fees"), + Self::AlreadyProcessed + => f.write_str("This transaction has already been processed"), + Self::BlockhashNotFound + => f.write_str("Blockhash not found"), + Self::InstructionError(idx, err) => write!(f, "Error processing Instruction {idx}: {err}"), + Self::CallChainTooDeep + => f.write_str("Loader call chain is too deep"), + Self::MissingSignatureForFee + => f.write_str("Transaction requires a fee but has no signature present"), + Self::InvalidAccountIndex + => f.write_str("Transaction contains an invalid account reference"), + Self::SignatureFailure + => f.write_str("Transaction did not pass signature verification"), + Self::InvalidProgramForExecution + => f.write_str("This program may not be used for executing instructions"), + Self::SanitizeFailure + => f.write_str("Transaction failed to sanitize accounts offsets correctly"), + Self::ClusterMaintenance + => f.write_str("Transactions are currently disabled due to cluster maintenance"), + Self::AccountBorrowOutstanding + => f.write_str("Transaction processing left an account with an outstanding borrowed reference"), + Self::WouldExceedMaxBlockCostLimit + => f.write_str("Transaction would exceed max Block Cost Limit"), + Self::UnsupportedVersion + => f.write_str("Transaction version is unsupported"), + Self::InvalidWritableAccount + => f.write_str("Transaction loads a writable account that cannot be written"), + Self::WouldExceedMaxAccountCostLimit + => f.write_str("Transaction would exceed max account limit within the block"), + Self::WouldExceedAccountDataBlockLimit + => f.write_str("Transaction would exceed account data limit within the block"), + Self::TooManyAccountLocks + => f.write_str("Transaction locked too many accounts"), + Self::AddressLookupTableNotFound + => f.write_str("Transaction loads an address table account that doesn't exist"), + Self::InvalidAddressLookupTableOwner + => f.write_str("Transaction loads an address table account with an invalid owner"), + Self::InvalidAddressLookupTableData + => f.write_str("Transaction loads an address table account with invalid data"), + Self::InvalidAddressLookupTableIndex + => f.write_str("Transaction address table lookup uses an invalid index"), + Self::InvalidRentPayingAccount + => f.write_str("Transaction leaves an account with a lower balance than rent-exempt minimum"), + Self::WouldExceedMaxVoteCostLimit + => f.write_str("Transaction would exceed max Vote Cost Limit"), + Self::WouldExceedAccountDataTotalLimit + => f.write_str("Transaction would exceed total account data limit"), + Self::DuplicateInstruction(idx) => write!(f, "Transaction contains a duplicate instruction ({idx}) that is not allowed"), + Self::InsufficientFundsForRent { + account_index + } => write!(f,"Transaction results in an account ({account_index}) with insufficient funds for rent"), + Self::MaxLoadedAccountsDataSizeExceeded + => f.write_str("Transaction exceeded max loaded accounts data size cap"), + Self::InvalidLoadedAccountsDataSizeLimit + => f.write_str("LoadedAccountsDataSizeLimit set for transaction must be greater than 0."), + Self::ResanitizationNeeded + => f.write_str("ResanitizationNeeded"), + Self::ProgramExecutionTemporarilyRestricted { + account_index + } => write!(f,"Execution of the program referenced by account at index {account_index} is temporarily restricted."), + Self::UnbalancedTransaction + => f.write_str("Sum of account balances before and after transaction do not match"), + Self::ProgramCacheHitMaxLimit + => f.write_str("Program cache hit max limit"), + } + } +} + +impl From for TransactionError { + fn from(_: SanitizeError) -> Self { + Self::SanitizeFailure + } +} + +#[cfg(not(target_os = "solana"))] +impl From for TransactionError { + fn from(err: SanitizeMessageError) -> Self { + match err { + SanitizeMessageError::AddressLoaderError(err) => Self::from(err), + _ => Self::SanitizeFailure, + } + } +} + +#[cfg(not(target_os = "solana"))] +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum AddressLoaderError { + /// Address loading from lookup tables is disabled + Disabled, + + /// Failed to load slot hashes sysvar + SlotHashesSysvarNotFound, + + /// Attempted to lookup addresses from a table that does not exist + LookupTableAccountNotFound, + + /// Attempted to lookup addresses from an account owned by the wrong program + InvalidAccountOwner, + + /// Attempted to lookup addresses from an invalid account + InvalidAccountData, + + /// Address lookup contains an invalid index + InvalidLookupIndex, +} + +#[cfg(not(target_os = "solana"))] +impl std::error::Error for AddressLoaderError {} + +#[cfg(not(target_os = "solana"))] +impl fmt::Display for AddressLoaderError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::Disabled => f.write_str("Address loading from lookup tables is disabled"), + Self::SlotHashesSysvarNotFound => f.write_str("Failed to load slot hashes sysvar"), + Self::LookupTableAccountNotFound => { + f.write_str("Attempted to lookup addresses from a table that does not exist") + } + Self::InvalidAccountOwner => f.write_str( + "Attempted to lookup addresses from an account owned by the wrong program", + ), + Self::InvalidAccountData => { + f.write_str("Attempted to lookup addresses from an invalid account") + } + Self::InvalidLookupIndex => f.write_str("Address lookup contains an invalid index"), + } + } +} + +#[cfg(not(target_os = "solana"))] +impl From for TransactionError { + fn from(err: AddressLoaderError) -> Self { + match err { + AddressLoaderError::Disabled => Self::UnsupportedVersion, + AddressLoaderError::SlotHashesSysvarNotFound => Self::AccountNotFound, + AddressLoaderError::LookupTableAccountNotFound => Self::AddressLookupTableNotFound, + AddressLoaderError::InvalidAccountOwner => Self::InvalidAddressLookupTableOwner, + AddressLoaderError::InvalidAccountData => Self::InvalidAddressLookupTableData, + AddressLoaderError::InvalidLookupIndex => Self::InvalidAddressLookupTableIndex, + } + } +} + +#[cfg(not(target_os = "solana"))] +#[derive(PartialEq, Debug, Eq, Clone)] +pub enum SanitizeMessageError { + IndexOutOfBounds, + ValueOutOfBounds, + InvalidValue, + AddressLoaderError(AddressLoaderError), +} + +#[cfg(not(target_os = "solana"))] +impl std::error::Error for SanitizeMessageError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Self::IndexOutOfBounds => None, + Self::ValueOutOfBounds => None, + Self::InvalidValue => None, + Self::AddressLoaderError(e) => Some(e), + } + } +} + +#[cfg(not(target_os = "solana"))] +impl fmt::Display for SanitizeMessageError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::IndexOutOfBounds => f.write_str("index out of bounds"), + Self::ValueOutOfBounds => f.write_str("value out of bounds"), + Self::InvalidValue => f.write_str("invalid value"), + Self::AddressLoaderError(e) => { + write!(f, "{e}") + } + } + } +} +#[cfg(not(target_os = "solana"))] +impl From for SanitizeMessageError { + fn from(source: AddressLoaderError) -> Self { + SanitizeMessageError::AddressLoaderError(source) + } +} + +#[cfg(not(target_os = "solana"))] +impl From for SanitizeMessageError { + fn from(err: SanitizeError) -> Self { + match err { + SanitizeError::IndexOutOfBounds => Self::IndexOutOfBounds, + SanitizeError::ValueOutOfBounds => Self::ValueOutOfBounds, + SanitizeError::InvalidValue => Self::InvalidValue, + } + } +} + +#[cfg(not(target_os = "solana"))] +#[derive(Debug)] +pub enum TransportError { + IoError(io::Error), + TransactionError(TransactionError), + Custom(String), +} + +#[cfg(not(target_os = "solana"))] +impl std::error::Error for TransportError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + TransportError::IoError(e) => Some(e), + TransportError::TransactionError(e) => Some(e), + TransportError::Custom(_) => None, + } + } +} + +#[cfg(not(target_os = "solana"))] +impl fmt::Display for TransportError { + fn fmt(&self, f: &mut fmt::Formatter) -> ::core::fmt::Result { + match self { + Self::IoError(e) => f.write_fmt(format_args!("transport io error: {e}")), + Self::TransactionError(e) => { + f.write_fmt(format_args!("transport transaction error: {e}")) + } + Self::Custom(s) => f.write_fmt(format_args!("transport custom error: {s}")), + } + } +} + +#[cfg(not(target_os = "solana"))] +impl From for TransportError { + fn from(e: io::Error) -> Self { + TransportError::IoError(e) + } +} + +#[cfg(not(target_os = "solana"))] +impl From for TransportError { + fn from(e: TransactionError) -> Self { + TransportError::TransactionError(e) + } +} + +#[cfg(not(target_os = "solana"))] +impl TransportError { + pub fn unwrap(&self) -> TransactionError { + if let TransportError::TransactionError(err) = self { + err.clone() + } else { + panic!("unexpected transport error") + } + } +} + +#[cfg(not(target_os = "solana"))] +pub type TransportResult = std::result::Result;