diff --git a/Cargo.lock b/Cargo.lock index 828859a9c..5c38b5d20 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2750,7 +2750,7 @@ dependencies = [ "solana-clock", "solana-frozen-abi", "solana-frozen-abi-macro", - "solana-instruction", + "solana-instruction-error", "solana-logger", "solana-pubkey", "solana-sdk-ids", @@ -2782,6 +2782,7 @@ dependencies = [ "solana-frozen-abi-macro", "solana-hash", "solana-instruction", + "solana-instruction-error", "solana-pubkey", "solana-sdk-ids", "solana-slot-hashes", @@ -2813,7 +2814,7 @@ version = "2.2.1" dependencies = [ "bincode", "serde", - "solana-instruction", + "solana-instruction-error", "solana-system-interface", ] @@ -3230,17 +3231,29 @@ dependencies = [ "borsh 1.5.5", "getrandom 0.2.15", "js-sys", - "num-traits", "serde", "serde_derive", "solana-define-syscall", "solana-frozen-abi", "solana-frozen-abi-macro", "solana-instruction", + "solana-instruction-error", "solana-pubkey", "wasm-bindgen", ] +[[package]] +name = "solana-instruction-error" +version = "1.0.0" +dependencies = [ + "num-traits", + "serde", + "serde_derive", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-program-error", +] + [[package]] name = "solana-instructions-sysvar" version = "2.2.2" @@ -3249,6 +3262,7 @@ dependencies = [ "qualifier_attr", "solana-account-info", "solana-instruction", + "solana-instruction-error", "solana-program-error", "solana-pubkey", "solana-sanitize", @@ -3547,6 +3561,7 @@ dependencies = [ "solana-frozen-abi-macro", "solana-hash", "solana-instruction", + "solana-instruction-error", "solana-instructions-sysvar", "solana-keccak-hasher", "solana-last-restart-slot", @@ -3590,13 +3605,9 @@ name = "solana-program-error" version = "2.2.2" dependencies = [ "borsh 1.5.5", - "num-traits", "num_enum", "serde", "serde_derive", - "solana-instruction", - "solana-msg", - "solana-pubkey", ] [[package]] @@ -3642,6 +3653,7 @@ dependencies = [ "solana-frozen-abi", "solana-frozen-abi-macro", "solana-program", + "solana-program-error", "solana-pubkey", "solana-sanitize", "solana-sha256-hasher", @@ -3869,7 +3881,7 @@ dependencies = [ "borsh 1.5.5", "rand 0.8.5", "serde", - "solana-instruction", + "solana-instruction-error", "solana-pubkey", "solana-sanitize", ] @@ -4116,7 +4128,7 @@ dependencies = [ "serde_derive", "solana-frozen-abi", "solana-frozen-abi-macro", - "solana-instruction", + "solana-instruction-error", "solana-sanitize", ] @@ -4142,6 +4154,7 @@ dependencies = [ "solana-frozen-abi-macro", "solana-hash", "solana-instruction", + "solana-instruction-error", "solana-logger", "solana-pubkey", "solana-rent", diff --git a/Cargo.toml b/Cargo.toml index 9264e9132..06f0ca873 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,7 @@ members = [ "hash", "inflation", "instruction", + "instruction-error", "instructions-sysvar", "keccak-hasher", "keypair", @@ -239,6 +240,7 @@ solana-hard-forks = { path = "hard-forks", version = "2.2.1", default-features = solana-hash = { path = "hash", version = "2.2.1", default-features = false } solana-inflation = { path = "inflation", version = "2.2.1" } solana-instruction = { path = "instruction", version = "2.3.0", default-features = false } +solana-instruction-error = { path = "instruction-error", version = "1.0.0" } solana-instructions-sysvar = { path = "instructions-sysvar", version = "2.2.1" } solana-keccak-hasher = { path = "keccak-hasher", version = "2.2.1" } solana-keypair = { path = "keypair", version = "2.2.1" } diff --git a/account/Cargo.toml b/account/Cargo.toml index 1534c8c38..2e04dc48b 100644 --- a/account/Cargo.toml +++ b/account/Cargo.toml @@ -15,12 +15,7 @@ all-features = true rustdoc-args = ["--cfg=docsrs"] [features] -bincode = [ - "dep:bincode", - "dep:solana-sysvar", - "solana-instruction/serde", - "serde", -] +bincode = ["dep:bincode", "dep:solana-sysvar", "serde"] dev-context-only-utils = ["bincode", "dep:qualifier_attr"] frozen-abi = [ "dep:solana-frozen-abi", @@ -45,7 +40,7 @@ solana-account-info = { workspace = true } solana-clock = { workspace = true } solana-frozen-abi = { workspace = true, optional = true } solana-frozen-abi-macro = { workspace = true, optional = true } -solana-instruction = { workspace = true } +solana-instruction-error = { workspace = true } solana-logger = { workspace = true, optional = true } solana-pubkey = { workspace = true } solana-sdk-ids = { workspace = true } diff --git a/account/src/lib.rs b/account/src/lib.rs index 1a9042c80..1561303e1 100644 --- a/account/src/lib.rs +++ b/account/src/lib.rs @@ -13,7 +13,7 @@ use solana_sysvar::Sysvar; use { solana_account_info::{debug_account_data::*, AccountInfo}, solana_clock::{Epoch, INITIAL_RENT_EPOCH}, - solana_instruction::error::LamportsError, + solana_instruction_error::LamportsError, solana_pubkey::Pubkey, solana_sdk_ids::{bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable, loader_v4}, std::{ diff --git a/account/src/state_traits.rs b/account/src/state_traits.rs index 9b44e43ea..a7852a9a6 100644 --- a/account/src/state_traits.rs +++ b/account/src/state_traits.rs @@ -3,7 +3,7 @@ use { crate::{Account, AccountSharedData}, bincode::ErrorKind, - solana_instruction::error::InstructionError, + solana_instruction_error::InstructionError, std::cell::Ref, }; diff --git a/address-lookup-table-interface/Cargo.toml b/address-lookup-table-interface/Cargo.toml index 814480e2c..17c527086 100644 --- a/address-lookup-table-interface/Cargo.toml +++ b/address-lookup-table-interface/Cargo.toml @@ -18,6 +18,7 @@ rustdoc-args = ["--cfg=docsrs"] bincode = [ "dep:bincode", "dep:solana-instruction", + "dep:solana-instruction-error", "serde", "solana-instruction/bincode", ] @@ -39,6 +40,7 @@ solana-frozen-abi-macro = { workspace = true, features = [ "frozen-abi", ], optional = true } solana-instruction = { workspace = true, features = ["std"], optional = true } +solana-instruction-error = { workspace = true, optional = true } solana-pubkey = { workspace = true } solana-sdk-ids = { workspace = true } solana-slot-hashes = { workspace = true } diff --git a/address-lookup-table-interface/src/state.rs b/address-lookup-table-interface/src/state.rs index 70e46a194..ace7790e8 100644 --- a/address-lookup-table-interface/src/state.rs +++ b/address-lookup-table-interface/src/state.rs @@ -3,7 +3,7 @@ use serde_derive::{Deserialize, Serialize}; #[cfg(feature = "frozen-abi")] use solana_frozen_abi_macro::{AbiEnumVisitor, AbiExample}; #[cfg(feature = "bincode")] -use solana_instruction::error::InstructionError; +use solana_instruction_error::InstructionError; use { crate::error::AddressLookupError, solana_clock::Slot, diff --git a/bincode/Cargo.toml b/bincode/Cargo.toml index a2d55f13f..087a6bed6 100644 --- a/bincode/Cargo.toml +++ b/bincode/Cargo.toml @@ -15,9 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] bincode = { workspace = true } serde = { workspace = true } -solana-instruction = { workspace = true, default-features = false, features = [ - "std", -] } +solana-instruction-error = { workspace = true } [dev-dependencies] solana-system-interface = { workspace = true, features = ["bincode"] } diff --git a/bincode/src/lib.rs b/bincode/src/lib.rs index 8170583f3..66e60a6b5 100644 --- a/bincode/src/lib.rs +++ b/bincode/src/lib.rs @@ -2,7 +2,7 @@ //! //! [bincode]: https://docs.rs/bincode -use {bincode::config::Options, solana_instruction::error::InstructionError}; +use {bincode::config::Options, solana_instruction_error::InstructionError}; /// Deserialize with a limit based the maximum amount of data a program can expect to get. /// This function should be used in place of direct deserialization to help prevent OOM errors diff --git a/cpi/Cargo.toml b/cpi/Cargo.toml index dcdd67133..1e4679788 100644 --- a/cpi/Cargo.toml +++ b/cpi/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] solana-account-info = { workspace = true } -solana-instruction = { workspace = true } +solana-instruction = { workspace = true, features = ["std"] } solana-program-error = { workspace = true } solana-pubkey = { workspace = true } diff --git a/instruction-error/Cargo.toml b/instruction-error/Cargo.toml new file mode 100644 index 000000000..9cd237f04 --- /dev/null +++ b/instruction-error/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "solana-instruction-error" +description = "Solana InstructionError type." +documentation = "https://docs.rs/solana-instruction-error" +version = "1.0.0" +authors = { workspace = true } +repository = { workspace = true } +homepage = { workspace = true } +license = { workspace = true } +edition = { workspace = true } + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] +all-features = true +rustdoc-args = ["--cfg=docsrs"] + +[features] +frozen-abi = [ + "dep:solana-frozen-abi", + "dep:solana-frozen-abi-macro", + "serde", + "std", +] +num-traits = ["dep:num-traits"] +serde = ["dep:serde", "dep:serde_derive"] +std = [] + +[dependencies] +num-traits = { workspace = true, optional = true } +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-program-error = { workspace = true } + +[lints] +workspace = true diff --git a/instruction/src/error.rs b/instruction-error/src/lib.rs similarity index 82% rename from instruction/src/error.rs rename to instruction-error/src/lib.rs index ea213cd85..99a6b1879 100644 --- a/instruction/src/error.rs +++ b/instruction-error/src/lib.rs @@ -1,52 +1,24 @@ +#![no_std] +#![cfg_attr(feature = "frozen-abi", feature(min_specialization))] use core::fmt; +#[cfg(feature = "num-traits")] +use num_traits::ToPrimitive; #[cfg(feature = "frozen-abi")] use solana_frozen_abi_macro::{AbiEnumVisitor, AbiExample}; #[cfg(feature = "std")] -use { - num_traits::ToPrimitive, - std::string::{String, ToString}, +extern crate std; +use solana_program_error::ProgramError; +pub use solana_program_error::{ + ACCOUNT_ALREADY_INITIALIZED, ACCOUNT_BORROW_FAILED, ACCOUNT_DATA_TOO_SMALL, + ACCOUNT_NOT_RENT_EXEMPT, ARITHMETIC_OVERFLOW, BORSH_IO_ERROR, + BUILTIN_PROGRAMS_MUST_CONSUME_COMPUTE_UNITS, CUSTOM_ZERO, ILLEGAL_OWNER, IMMUTABLE, + INCORRECT_AUTHORITY, INCORRECT_PROGRAM_ID, INSUFFICIENT_FUNDS, INVALID_ACCOUNT_DATA, + INVALID_ACCOUNT_DATA_REALLOC, INVALID_ACCOUNT_OWNER, INVALID_ARGUMENT, + INVALID_INSTRUCTION_DATA, INVALID_SEEDS, MAX_ACCOUNTS_DATA_ALLOCATIONS_EXCEEDED, + MAX_INSTRUCTION_TRACE_LENGTH_EXCEEDED, MAX_SEED_LENGTH_EXCEEDED, MISSING_REQUIRED_SIGNATURES, + NOT_ENOUGH_ACCOUNT_KEYS, UNINITIALIZED_ACCOUNT, UNSUPPORTED_SYSVAR, }; -/// Builtin return values occupy the upper 32 bits -const BUILTIN_BIT_SHIFT: usize = 32; -macro_rules! to_builtin { - ($error:expr) => { - ($error as u64) << BUILTIN_BIT_SHIFT - }; -} - -pub const CUSTOM_ZERO: u64 = to_builtin!(1); -pub const INVALID_ARGUMENT: u64 = to_builtin!(2); -pub const INVALID_INSTRUCTION_DATA: u64 = to_builtin!(3); -pub const INVALID_ACCOUNT_DATA: u64 = to_builtin!(4); -pub const ACCOUNT_DATA_TOO_SMALL: u64 = to_builtin!(5); -pub const INSUFFICIENT_FUNDS: u64 = to_builtin!(6); -pub const INCORRECT_PROGRAM_ID: u64 = to_builtin!(7); -pub const MISSING_REQUIRED_SIGNATURES: u64 = to_builtin!(8); -pub const ACCOUNT_ALREADY_INITIALIZED: u64 = to_builtin!(9); -pub const UNINITIALIZED_ACCOUNT: u64 = to_builtin!(10); -pub const NOT_ENOUGH_ACCOUNT_KEYS: u64 = to_builtin!(11); -pub const ACCOUNT_BORROW_FAILED: u64 = to_builtin!(12); -pub const MAX_SEED_LENGTH_EXCEEDED: u64 = to_builtin!(13); -pub const INVALID_SEEDS: u64 = to_builtin!(14); -pub const BORSH_IO_ERROR: u64 = to_builtin!(15); -pub const ACCOUNT_NOT_RENT_EXEMPT: u64 = to_builtin!(16); -pub const UNSUPPORTED_SYSVAR: u64 = to_builtin!(17); -pub const ILLEGAL_OWNER: u64 = to_builtin!(18); -pub const MAX_ACCOUNTS_DATA_ALLOCATIONS_EXCEEDED: u64 = to_builtin!(19); -pub const INVALID_ACCOUNT_DATA_REALLOC: u64 = to_builtin!(20); -pub const MAX_INSTRUCTION_TRACE_LENGTH_EXCEEDED: u64 = to_builtin!(21); -pub const BUILTIN_PROGRAMS_MUST_CONSUME_COMPUTE_UNITS: u64 = to_builtin!(22); -pub const INVALID_ACCOUNT_OWNER: u64 = to_builtin!(23); -pub const ARITHMETIC_OVERFLOW: u64 = to_builtin!(24); -pub const IMMUTABLE: u64 = to_builtin!(25); -pub const INCORRECT_AUTHORITY: u64 = to_builtin!(26); -// Warning: Any new error codes added here must also be: -// - Added to the below conversions -// - Added as an equivalent to ProgramError and InstructionError -// - Be featurized in the BPF loader to return `InstructionError::InvalidError` -// until the feature is activated - /// Reasons the runtime might have rejected an instruction. /// /// Members of this enum must not be removed, but new ones can be added. @@ -54,7 +26,6 @@ pub const INCORRECT_AUTHORITY: u64 = to_builtin!(26); /// an error be consistent across software versions. For example, it is /// dangerous to include error strings from 3rd party crates because they could /// change at any time and changes to them are difficult to detect. -#[cfg(feature = "std")] #[cfg_attr(feature = "frozen-abi", derive(AbiExample, AbiEnumVisitor))] #[cfg_attr( feature = "serde", @@ -202,15 +173,7 @@ pub enum InstructionError { IncorrectAuthority, /// Failed to serialize or deserialize account data - /// - /// Warning: This error should never be emitted by the runtime. - /// - /// This error includes strings from the underlying 3rd party Borsh crate - /// which can be dangerous because the error strings could change across - /// Borsh versions. Only programs can use this error because they are - /// consistent across Solana software versions. - /// - BorshIoError(String), + BorshIoError, /// An account does not have enough lamports to be rent-exempt AccountNotRentExempt, @@ -245,7 +208,6 @@ pub enum InstructionError { #[cfg(feature = "std")] impl std::error::Error for InstructionError {} -#[cfg(feature = "std")] impl fmt::Display for InstructionError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { @@ -361,8 +323,8 @@ impl fmt::Display for InstructionError { InstructionError::ProgramFailedToCompile => f.write_str("Program failed to compile"), InstructionError::Immutable => f.write_str("Account is immutable"), InstructionError::IncorrectAuthority => f.write_str("Incorrect authority provided"), - InstructionError::BorshIoError(s) => { - write!(f, "Failed to serialize or deserialize account data: {s}",) + InstructionError::BorshIoError => { + f.write_str("Failed to serialize or deserialize account data") } InstructionError::AccountNotRentExempt => { f.write_str("An account does not have enough lamports to be rent-exempt") @@ -385,7 +347,7 @@ impl fmt::Display for InstructionError { } } -#[cfg(feature = "std")] +#[cfg(feature = "num-traits")] impl From for InstructionError where T: ToPrimitive, @@ -407,7 +369,7 @@ where ACCOUNT_BORROW_FAILED => Self::AccountBorrowFailed, MAX_SEED_LENGTH_EXCEEDED => Self::MaxSeedLengthExceeded, INVALID_SEEDS => Self::InvalidSeeds, - BORSH_IO_ERROR => Self::BorshIoError("Unknown".to_string()), + BORSH_IO_ERROR => Self::BorshIoError, ACCOUNT_NOT_RENT_EXEMPT => Self::AccountNotRentExempt, UNSUPPORTED_SYSVAR => Self::UnsupportedSysvar, ILLEGAL_OWNER => Self::IllegalOwner, @@ -423,7 +385,7 @@ where INCORRECT_AUTHORITY => Self::IncorrectAuthority, _ => { // A valid custom error has no bits set in the upper 32 - if error >> BUILTIN_BIT_SHIFT == 0 { + if error >> solana_program_error::BUILTIN_BIT_SHIFT == 0 { Self::Custom(error as u32) } else { Self::InvalidError @@ -453,7 +415,6 @@ impl fmt::Display for LamportsError { } } -#[cfg(feature = "std")] impl From for InstructionError { fn from(error: LamportsError) -> Self { match error { @@ -462,3 +423,45 @@ impl From for InstructionError { } } } + +impl TryFrom for ProgramError { + type Error = InstructionError; + + fn try_from(error: InstructionError) -> Result { + match error { + Self::Error::Custom(err) => Ok(Self::Custom(err)), + Self::Error::InvalidArgument => Ok(Self::InvalidArgument), + Self::Error::InvalidInstructionData => Ok(Self::InvalidInstructionData), + Self::Error::InvalidAccountData => Ok(Self::InvalidAccountData), + Self::Error::AccountDataTooSmall => Ok(Self::AccountDataTooSmall), + Self::Error::InsufficientFunds => Ok(Self::InsufficientFunds), + Self::Error::IncorrectProgramId => Ok(Self::IncorrectProgramId), + Self::Error::MissingRequiredSignature => Ok(Self::MissingRequiredSignature), + Self::Error::AccountAlreadyInitialized => Ok(Self::AccountAlreadyInitialized), + Self::Error::UninitializedAccount => Ok(Self::UninitializedAccount), + Self::Error::NotEnoughAccountKeys => Ok(Self::NotEnoughAccountKeys), + Self::Error::AccountBorrowFailed => Ok(Self::AccountBorrowFailed), + Self::Error::MaxSeedLengthExceeded => Ok(Self::MaxSeedLengthExceeded), + Self::Error::InvalidSeeds => Ok(Self::InvalidSeeds), + Self::Error::BorshIoError => Ok(Self::BorshIoError), + Self::Error::AccountNotRentExempt => Ok(Self::AccountNotRentExempt), + Self::Error::UnsupportedSysvar => Ok(Self::UnsupportedSysvar), + Self::Error::IllegalOwner => Ok(Self::IllegalOwner), + Self::Error::MaxAccountsDataAllocationsExceeded => { + Ok(Self::MaxAccountsDataAllocationsExceeded) + } + Self::Error::InvalidRealloc => Ok(Self::InvalidRealloc), + Self::Error::MaxInstructionTraceLengthExceeded => { + Ok(Self::MaxInstructionTraceLengthExceeded) + } + Self::Error::BuiltinProgramsMustConsumeComputeUnits => { + Ok(Self::BuiltinProgramsMustConsumeComputeUnits) + } + Self::Error::InvalidAccountOwner => Ok(Self::InvalidAccountOwner), + Self::Error::ArithmeticOverflow => Ok(Self::ArithmeticOverflow), + Self::Error::Immutable => Ok(Self::Immutable), + Self::Error::IncorrectAuthority => Ok(Self::IncorrectAuthority), + _ => Err(error), + } + } +} diff --git a/instruction/Cargo.toml b/instruction/Cargo.toml index 199f03fed..2af715cf2 100644 --- a/instruction/Cargo.toml +++ b/instruction/Cargo.toml @@ -31,11 +31,11 @@ syscalls = ["std"] [dependencies] bincode = { workspace = true, optional = true } borsh = { workspace = true, optional = true } -num-traits = { workspace = true } 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-error = { workspace = true, features = ["num-traits"] } solana-pubkey = { workspace = true, default-features = false } [target.'cfg(target_arch = "wasm32")'.dependencies] diff --git a/instruction/src/lib.rs b/instruction/src/lib.rs index 899cf9d2a..82c0011ae 100644 --- a/instruction/src/lib.rs +++ b/instruction/src/lib.rs @@ -11,7 +11,6 @@ //! [`AccountMeta`] values. The runtime uses this information to efficiently //! schedule execution of transactions. #![cfg_attr(docsrs, feature(doc_auto_cfg))] -#![cfg_attr(feature = "frozen-abi", feature(min_specialization))] #![allow(clippy::arithmetic_side_effects)] #![no_std] @@ -23,7 +22,7 @@ use std::vec::Vec; pub mod account_meta; #[cfg(feature = "std")] pub use account_meta::AccountMeta; -pub mod error; +pub use solana_instruction_error as error; #[cfg(any(feature = "syscalls", target_os = "solana"))] pub mod syscalls; #[cfg(all(feature = "std", target_arch = "wasm32"))] diff --git a/instructions-sysvar/Cargo.toml b/instructions-sysvar/Cargo.toml index 4516436d5..2cee4309f 100644 --- a/instructions-sysvar/Cargo.toml +++ b/instructions-sysvar/Cargo.toml @@ -20,7 +20,8 @@ dev-context-only-utils = ["dep:qualifier_attr"] [dependencies] qualifier_attr = { workspace = true, optional = true } solana-account-info = { workspace = true } -solana-instruction = { workspace = true, default-features = false } +solana-instruction = { workspace = true, default-features = false, features = ["std"] } +solana-instruction-error = { workspace = true } solana-program-error = { workspace = true } solana-pubkey = { workspace = true, default-features = false } solana-sanitize = { workspace = true } diff --git a/instructions-sysvar/src/lib.rs b/instructions-sysvar/src/lib.rs index 17e6c877d..53ee833e3 100644 --- a/instructions-sysvar/src/lib.rs +++ b/instructions-sysvar/src/lib.rs @@ -41,7 +41,8 @@ use { }; use { solana_account_info::AccountInfo, - solana_instruction::{error::InstructionError, AccountMeta, Instruction}, + solana_instruction::{AccountMeta, Instruction}, + solana_instruction_error::InstructionError, solana_program_error::ProgramError, solana_sanitize::SanitizeError, solana_serialize_utils::{read_pubkey, read_slice, read_u16, read_u8}, diff --git a/program-error/Cargo.toml b/program-error/Cargo.toml index 6488205a5..34ac40898 100644 --- a/program-error/Cargo.toml +++ b/program-error/Cargo.toml @@ -17,17 +17,12 @@ rustdoc-args = ["--cfg=docsrs"] [features] borsh = ["dep:borsh"] serde = ["dep:serde", "dep:serde_derive"] +std = [] [dependencies] borsh = { workspace = true, optional = true } -num-traits = { workspace = true } serde = { workspace = true, optional = true } serde_derive = { workspace = true, optional = true } -solana-instruction = { workspace = true, default-features = false, features = [ - "std", -] } -solana-msg = { workspace = true } -solana-pubkey = { workspace = true, default-features = false } [dev-dependencies] num_enum = { workspace = true } diff --git a/program-error/src/lib.rs b/program-error/src/lib.rs index b936d9dd6..446ade8b9 100644 --- a/program-error/src/lib.rs +++ b/program-error/src/lib.rs @@ -2,30 +2,56 @@ #![allow(clippy::arithmetic_side_effects)] #![cfg_attr(docsrs, feature(doc_auto_cfg))] +#![no_std] +#[cfg(feature = "std")] +extern crate std; #[cfg(feature = "borsh")] use borsh::io::Error as BorshIoError; +use core::{convert::TryFrom, fmt}; #[cfg(feature = "serde")] use serde_derive::{Deserialize, Serialize}; -use { - core::fmt, - num_traits::FromPrimitive, - solana_instruction::error::{ - InstructionError, ACCOUNT_ALREADY_INITIALIZED, ACCOUNT_BORROW_FAILED, - ACCOUNT_DATA_TOO_SMALL, ACCOUNT_NOT_RENT_EXEMPT, ARITHMETIC_OVERFLOW, BORSH_IO_ERROR, - BUILTIN_PROGRAMS_MUST_CONSUME_COMPUTE_UNITS, CUSTOM_ZERO, ILLEGAL_OWNER, IMMUTABLE, - INCORRECT_AUTHORITY, INCORRECT_PROGRAM_ID, INSUFFICIENT_FUNDS, INVALID_ACCOUNT_DATA, - INVALID_ACCOUNT_DATA_REALLOC, INVALID_ACCOUNT_OWNER, INVALID_ARGUMENT, - INVALID_INSTRUCTION_DATA, INVALID_SEEDS, MAX_ACCOUNTS_DATA_ALLOCATIONS_EXCEEDED, - MAX_INSTRUCTION_TRACE_LENGTH_EXCEEDED, MAX_SEED_LENGTH_EXCEEDED, - MISSING_REQUIRED_SIGNATURES, NOT_ENOUGH_ACCOUNT_KEYS, UNINITIALIZED_ACCOUNT, - UNSUPPORTED_SYSVAR, - }, - solana_msg::msg, - solana_pubkey::PubkeyError, - std::convert::TryFrom, -}; -pub type ProgramResult = std::result::Result<(), ProgramError>; +pub type ProgramResult = core::result::Result<(), ProgramError>; + +/// Builtin return values occupy the upper 32 bits +pub const BUILTIN_BIT_SHIFT: usize = 32; +macro_rules! to_builtin { + ($error:expr) => { + ($error as u64) << BUILTIN_BIT_SHIFT + }; +} + +pub const CUSTOM_ZERO: u64 = to_builtin!(1); +pub const INVALID_ARGUMENT: u64 = to_builtin!(2); +pub const INVALID_INSTRUCTION_DATA: u64 = to_builtin!(3); +pub const INVALID_ACCOUNT_DATA: u64 = to_builtin!(4); +pub const ACCOUNT_DATA_TOO_SMALL: u64 = to_builtin!(5); +pub const INSUFFICIENT_FUNDS: u64 = to_builtin!(6); +pub const INCORRECT_PROGRAM_ID: u64 = to_builtin!(7); +pub const MISSING_REQUIRED_SIGNATURES: u64 = to_builtin!(8); +pub const ACCOUNT_ALREADY_INITIALIZED: u64 = to_builtin!(9); +pub const UNINITIALIZED_ACCOUNT: u64 = to_builtin!(10); +pub const NOT_ENOUGH_ACCOUNT_KEYS: u64 = to_builtin!(11); +pub const ACCOUNT_BORROW_FAILED: u64 = to_builtin!(12); +pub const MAX_SEED_LENGTH_EXCEEDED: u64 = to_builtin!(13); +pub const INVALID_SEEDS: u64 = to_builtin!(14); +pub const BORSH_IO_ERROR: u64 = to_builtin!(15); +pub const ACCOUNT_NOT_RENT_EXEMPT: u64 = to_builtin!(16); +pub const UNSUPPORTED_SYSVAR: u64 = to_builtin!(17); +pub const ILLEGAL_OWNER: u64 = to_builtin!(18); +pub const MAX_ACCOUNTS_DATA_ALLOCATIONS_EXCEEDED: u64 = to_builtin!(19); +pub const INVALID_ACCOUNT_DATA_REALLOC: u64 = to_builtin!(20); +pub const MAX_INSTRUCTION_TRACE_LENGTH_EXCEEDED: u64 = to_builtin!(21); +pub const BUILTIN_PROGRAMS_MUST_CONSUME_COMPUTE_UNITS: u64 = to_builtin!(22); +pub const INVALID_ACCOUNT_OWNER: u64 = to_builtin!(23); +pub const ARITHMETIC_OVERFLOW: u64 = to_builtin!(24); +pub const IMMUTABLE: u64 = to_builtin!(25); +pub const INCORRECT_AUTHORITY: u64 = to_builtin!(26); +// Warning: Any new error codes added here must also be: +// - Added to the below conversions +// - Added as an equivalent to ProgramError and InstructionError +// - Be featurized in the BPF loader to return `InstructionError::InvalidError` +// until the feature is activated /// Reasons the program may fail #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] @@ -48,7 +74,7 @@ pub enum ProgramError { AccountBorrowFailed, MaxSeedLengthExceeded, InvalidSeeds, - BorshIoError(String), + BorshIoError, AccountNotRentExempt, UnsupportedSysvar, IllegalOwner, @@ -62,6 +88,7 @@ pub enum ProgramError { IncorrectAuthority, } +#[cfg(feature = "std")] impl std::error::Error for ProgramError {} impl fmt::Display for ProgramError { @@ -94,7 +121,7 @@ impl fmt::Display for ProgramError { => f.write_str("Length of the seed is too long for address generation"), ProgramError::InvalidSeeds => f.write_str("Provided seeds do not result in a valid address"), - ProgramError::BorshIoError(s) => write!(f, "IO Error: {s}"), + ProgramError::BorshIoError => f.write_str("IO Error"), ProgramError::AccountNotRentExempt => f.write_str("An account does not have enough lamports to be rent-exempt"), ProgramError::UnsupportedSysvar @@ -121,66 +148,6 @@ impl fmt::Display for ProgramError { } } -#[deprecated( - since = "2.2.2", - note = "Use `ToStr` instead with `solana_msg::msg!` or any other logging" -)] -#[allow(deprecated)] -pub trait PrintProgramError { - fn print(&self) - where - E: 'static + std::error::Error + PrintProgramError + FromPrimitive; -} - -#[allow(deprecated)] -impl PrintProgramError for ProgramError { - fn print(&self) - where - E: 'static + std::error::Error + PrintProgramError + FromPrimitive, - { - match self { - Self::Custom(error) => { - if let Some(custom_error) = E::from_u32(*error) { - custom_error.print::(); - } else { - msg!("Error: Unknown"); - } - } - Self::InvalidArgument => msg!("Error: InvalidArgument"), - Self::InvalidInstructionData => msg!("Error: InvalidInstructionData"), - Self::InvalidAccountData => msg!("Error: InvalidAccountData"), - Self::AccountDataTooSmall => msg!("Error: AccountDataTooSmall"), - Self::InsufficientFunds => msg!("Error: InsufficientFunds"), - Self::IncorrectProgramId => msg!("Error: IncorrectProgramId"), - Self::MissingRequiredSignature => msg!("Error: MissingRequiredSignature"), - Self::AccountAlreadyInitialized => msg!("Error: AccountAlreadyInitialized"), - Self::UninitializedAccount => msg!("Error: UninitializedAccount"), - Self::NotEnoughAccountKeys => msg!("Error: NotEnoughAccountKeys"), - Self::AccountBorrowFailed => msg!("Error: AccountBorrowFailed"), - Self::MaxSeedLengthExceeded => msg!("Error: MaxSeedLengthExceeded"), - Self::InvalidSeeds => msg!("Error: InvalidSeeds"), - Self::BorshIoError(_) => msg!("Error: BorshIoError"), - Self::AccountNotRentExempt => msg!("Error: AccountNotRentExempt"), - Self::UnsupportedSysvar => msg!("Error: UnsupportedSysvar"), - Self::IllegalOwner => msg!("Error: IllegalOwner"), - Self::MaxAccountsDataAllocationsExceeded => { - msg!("Error: MaxAccountsDataAllocationsExceeded") - } - Self::InvalidRealloc => msg!("Error: InvalidRealloc"), - Self::MaxInstructionTraceLengthExceeded => { - msg!("Error: MaxInstructionTraceLengthExceeded") - } - Self::BuiltinProgramsMustConsumeComputeUnits => { - msg!("Error: BuiltinProgramsMustConsumeComputeUnits") - } - Self::InvalidAccountOwner => msg!("Error: InvalidAccountOwner"), - Self::ArithmeticOverflow => msg!("Error: ArithmeticOverflow"), - Self::Immutable => msg!("Error: Immutable"), - Self::IncorrectAuthority => msg!("Error: IncorrectAuthority"), - } - } -} - /// A trait for converting a program's specific error type to a `&str`. /// /// Can be used with `ProgramError::to_str::()` to get an error string @@ -246,7 +213,7 @@ impl ProgramError { Self::AccountBorrowFailed => "Error: AccountBorrowFailed", Self::MaxSeedLengthExceeded => "Error: MaxSeedLengthExceeded", Self::InvalidSeeds => "Error: InvalidSeeds", - Self::BorshIoError(_) => "Error: BorshIoError", + Self::BorshIoError => "Error: BorshIoError", Self::AccountNotRentExempt => "Error: AccountNotRentExempt", Self::UnsupportedSysvar => "Error: UnsupportedSysvar", Self::IllegalOwner => "Error: IllegalOwner", @@ -280,7 +247,7 @@ impl From for u64 { ProgramError::AccountBorrowFailed => ACCOUNT_BORROW_FAILED, ProgramError::MaxSeedLengthExceeded => MAX_SEED_LENGTH_EXCEEDED, ProgramError::InvalidSeeds => INVALID_SEEDS, - ProgramError::BorshIoError(_) => BORSH_IO_ERROR, + ProgramError::BorshIoError => BORSH_IO_ERROR, ProgramError::AccountNotRentExempt => ACCOUNT_NOT_RENT_EXEMPT, ProgramError::UnsupportedSysvar => UNSUPPORTED_SYSVAR, ProgramError::IllegalOwner => ILLEGAL_OWNER, @@ -326,7 +293,7 @@ impl From for ProgramError { ACCOUNT_BORROW_FAILED => Self::AccountBorrowFailed, MAX_SEED_LENGTH_EXCEEDED => Self::MaxSeedLengthExceeded, INVALID_SEEDS => Self::InvalidSeeds, - BORSH_IO_ERROR => Self::BorshIoError("Unknown".to_string()), + BORSH_IO_ERROR => Self::BorshIoError, ACCOUNT_NOT_RENT_EXEMPT => Self::AccountNotRentExempt, UNSUPPORTED_SYSVAR => Self::UnsupportedSysvar, ILLEGAL_OWNER => Self::IllegalOwner, @@ -345,61 +312,9 @@ impl From for ProgramError { } } -impl TryFrom for ProgramError { - type Error = InstructionError; - - fn try_from(error: InstructionError) -> Result { - match error { - Self::Error::Custom(err) => Ok(Self::Custom(err)), - Self::Error::InvalidArgument => Ok(Self::InvalidArgument), - Self::Error::InvalidInstructionData => Ok(Self::InvalidInstructionData), - Self::Error::InvalidAccountData => Ok(Self::InvalidAccountData), - Self::Error::AccountDataTooSmall => Ok(Self::AccountDataTooSmall), - Self::Error::InsufficientFunds => Ok(Self::InsufficientFunds), - Self::Error::IncorrectProgramId => Ok(Self::IncorrectProgramId), - Self::Error::MissingRequiredSignature => Ok(Self::MissingRequiredSignature), - Self::Error::AccountAlreadyInitialized => Ok(Self::AccountAlreadyInitialized), - Self::Error::UninitializedAccount => Ok(Self::UninitializedAccount), - Self::Error::NotEnoughAccountKeys => Ok(Self::NotEnoughAccountKeys), - Self::Error::AccountBorrowFailed => Ok(Self::AccountBorrowFailed), - Self::Error::MaxSeedLengthExceeded => Ok(Self::MaxSeedLengthExceeded), - Self::Error::InvalidSeeds => Ok(Self::InvalidSeeds), - Self::Error::BorshIoError(err) => Ok(Self::BorshIoError(err)), - Self::Error::AccountNotRentExempt => Ok(Self::AccountNotRentExempt), - Self::Error::UnsupportedSysvar => Ok(Self::UnsupportedSysvar), - Self::Error::IllegalOwner => Ok(Self::IllegalOwner), - Self::Error::MaxAccountsDataAllocationsExceeded => { - Ok(Self::MaxAccountsDataAllocationsExceeded) - } - Self::Error::InvalidRealloc => Ok(Self::InvalidRealloc), - Self::Error::MaxInstructionTraceLengthExceeded => { - Ok(Self::MaxInstructionTraceLengthExceeded) - } - Self::Error::BuiltinProgramsMustConsumeComputeUnits => { - Ok(Self::BuiltinProgramsMustConsumeComputeUnits) - } - Self::Error::InvalidAccountOwner => Ok(Self::InvalidAccountOwner), - Self::Error::ArithmeticOverflow => Ok(Self::ArithmeticOverflow), - Self::Error::Immutable => Ok(Self::Immutable), - Self::Error::IncorrectAuthority => Ok(Self::IncorrectAuthority), - _ => Err(error), - } - } -} - -impl From for ProgramError { - fn from(error: PubkeyError) -> Self { - match error { - PubkeyError::MaxSeedLengthExceeded => Self::MaxSeedLengthExceeded, - PubkeyError::InvalidSeeds => Self::InvalidSeeds, - PubkeyError::IllegalOwner => Self::IllegalOwner, - } - } -} - #[cfg(feature = "borsh")] impl From for ProgramError { - fn from(error: BorshIoError) -> Self { - Self::BorshIoError(format!("{error}")) + fn from(_error: BorshIoError) -> Self { + Self::BorshIoError } } diff --git a/program/Cargo.toml b/program/Cargo.toml index a98febdc1..6e3a6be49 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -82,6 +82,7 @@ solana-instruction = { workspace = true, default-features = false, features = [ "std", "syscalls", ] } +solana-instruction-error = { workspace = true, features = ["num-traits"] } solana-instructions-sysvar = { workspace = true } solana-keccak-hasher = { workspace = true, features = ["sha3"] } solana-last-restart-slot = { workspace = true, features = ["serde", "sysvar"] } diff --git a/program/src/lamports.rs b/program/src/lamports.rs index 925240b98..87cc2b6d3 100644 --- a/program/src/lamports.rs +++ b/program/src/lamports.rs @@ -1,6 +1,6 @@ //! Re-exports the [`LamportsError`] type for backwards compatibility. #[deprecated( since = "2.1.0", - note = "Use solana_instruction::error::LamportsError instead" + note = "Use solana_instruction_error::LamportsError instead" )] -pub use solana_instruction::error::LamportsError; +pub use solana_instruction_error::LamportsError; diff --git a/program/src/program_error.rs b/program/src/program_error.rs index 8c2901583..975a5e312 100644 --- a/program/src/program_error.rs +++ b/program/src/program_error.rs @@ -1,7 +1,5 @@ -#[allow(deprecated)] -pub use solana_program_error::PrintProgramError; pub use { - solana_instruction::error::{ + solana_instruction_error::{ ACCOUNT_ALREADY_INITIALIZED, ACCOUNT_BORROW_FAILED, ACCOUNT_DATA_TOO_SMALL, ACCOUNT_NOT_RENT_EXEMPT, ARITHMETIC_OVERFLOW, BORSH_IO_ERROR, BUILTIN_PROGRAMS_MUST_CONSUME_COMPUTE_UNITS, CUSTOM_ZERO, ILLEGAL_OWNER, IMMUTABLE, diff --git a/pubkey/Cargo.toml b/pubkey/Cargo.toml index a7fc27149..b140f4e88 100644 --- a/pubkey/Cargo.toml +++ b/pubkey/Cargo.toml @@ -45,6 +45,7 @@ solana-frozen-abi = { workspace = true, optional = true, features = [ solana-frozen-abi-macro = { workspace = true, optional = true, features = [ "frozen-abi", ] } +solana-program-error = { workspace = true } solana-sanitize = { workspace = true } [target.'cfg(not(target_os = "solana"))'.dependencies] diff --git a/pubkey/src/lib.rs b/pubkey/src/lib.rs index 6c26bd646..fbe42d0e4 100644 --- a/pubkey/src/lib.rs +++ b/pubkey/src/lib.rs @@ -29,6 +29,7 @@ use { str::{from_utf8_unchecked, FromStr}, }, num_traits::{FromPrimitive, ToPrimitive}, + solana_program_error::ProgramError, }; #[cfg(target_arch = "wasm32")] use { @@ -59,7 +60,7 @@ const SUCCESS: u64 = 0; // Use strum when testing to ensure our FromPrimitive // impl is exhaustive #[cfg_attr(test, derive(strum_macros::FromRepr, strum_macros::EnumIter))] -#[cfg_attr(feature = "serde", derive(Serialize))] +#[cfg_attr(feature = "serde", derive(serde_derive::Serialize))] #[derive(Debug, Clone, PartialEq, Eq)] pub enum PubkeyError { /// Length of the seed is too long for address generation @@ -130,6 +131,16 @@ impl From for PubkeyError { } } +impl From for ProgramError { + fn from(error: PubkeyError) -> Self { + match error { + PubkeyError::MaxSeedLengthExceeded => Self::MaxSeedLengthExceeded, + PubkeyError::InvalidSeeds => Self::InvalidSeeds, + PubkeyError::IllegalOwner => Self::IllegalOwner, + } + } +} + /// The address of a [Solana account][acc]. /// /// Some account addresses are [ed25519] public keys, with corresponding secret diff --git a/serialize-utils/Cargo.toml b/serialize-utils/Cargo.toml index e29090c52..071e1903c 100644 --- a/serialize-utils/Cargo.toml +++ b/serialize-utils/Cargo.toml @@ -13,9 +13,7 @@ edition = { workspace = true } targets = ["x86_64-unknown-linux-gnu"] [dependencies] -solana-instruction = { workspace = true, default-features = false, features = [ - "std", -] } +solana-instruction-error = { workspace = true } solana-pubkey = { workspace = true, default-features = false } solana-sanitize = { workspace = true } diff --git a/serialize-utils/src/cursor.rs b/serialize-utils/src/cursor.rs index 5e4639281..e2a49576e 100644 --- a/serialize-utils/src/cursor.rs +++ b/serialize-utils/src/cursor.rs @@ -1,5 +1,5 @@ use { - solana_instruction::error::InstructionError, + solana_instruction_error::InstructionError, solana_pubkey::{Pubkey, PUBKEY_BYTES}, std::{ io::{BufRead as _, Cursor, Read}, diff --git a/transaction-error/Cargo.toml b/transaction-error/Cargo.toml index 0443a7fdd..064923297 100644 --- a/transaction-error/Cargo.toml +++ b/transaction-error/Cargo.toml @@ -16,16 +16,14 @@ rustdoc-args = ["--cfg=docsrs"] [features] frozen-abi = ["dep:solana-frozen-abi", "dep:solana-frozen-abi-macro"] -serde = ["dep:serde", "dep:serde_derive", "solana-instruction/serde"] +serde = ["dep:serde", "dep:serde_derive", "solana-instruction-error/serde"] [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-instruction-error = { workspace = true } solana-sanitize = { workspace = true } [lints] diff --git a/transaction-error/src/lib.rs b/transaction-error/src/lib.rs index 2f5b72b96..5b1082157 100644 --- a/transaction-error/src/lib.rs +++ b/transaction-error/src/lib.rs @@ -4,7 +4,7 @@ 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}; +use {core::fmt, solana_instruction_error::InstructionError, solana_sanitize::SanitizeError}; pub type TransactionResult = Result; diff --git a/vote-interface/Cargo.toml b/vote-interface/Cargo.toml index 53c284bf9..e6e76f324 100644 --- a/vote-interface/Cargo.toml +++ b/vote-interface/Cargo.toml @@ -59,6 +59,7 @@ solana-frozen-abi-macro = { workspace = true, features = [ ], optional = true } solana-hash = { workspace = true } solana-instruction = { workspace = true, features = ["std"] } +solana-instruction-error = { workspace = true, features = ["num-traits"] } solana-pubkey = { workspace = true } solana-rent = { workspace = true } solana-sdk-ids = { workspace = true } diff --git a/vote-interface/src/state/mod.rs b/vote-interface/src/state/mod.rs index e98ecfdd6..c885a9366 100644 --- a/vote-interface/src/state/mod.rs +++ b/vote-interface/src/state/mod.rs @@ -391,7 +391,7 @@ mod tests { use { super::*, crate::error::VoteError, bincode::serialized_size, core::mem::MaybeUninit, itertools::Itertools, rand::Rng, solana_clock::Clock, solana_hash::Hash, - solana_instruction::error::InstructionError, + solana_instruction_error::InstructionError, }; #[test] diff --git a/vote-interface/src/state/vote_state_v3.rs b/vote-interface/src/state/vote_state_v3.rs index c12ee6a24..a90797ba4 100644 --- a/vote-interface/src/state/vote_state_v3.rs +++ b/vote-interface/src/state/vote_state_v3.rs @@ -15,7 +15,7 @@ use { authorized_voters::AuthorizedVoters, error::VoteError, state::DEFAULT_PRIOR_VOTERS_OFFSET, }, solana_clock::{Clock, Epoch, Slot, UnixTimestamp}, - solana_instruction::error::InstructionError, + solana_instruction_error::InstructionError, solana_pubkey::Pubkey, solana_rent::Rent, std::{collections::VecDeque, fmt::Debug}, @@ -608,7 +608,7 @@ mod vote_state_deserialize { }, }, solana_clock::Epoch, - solana_instruction::error::InstructionError, + solana_instruction_error::InstructionError, solana_pubkey::Pubkey, solana_serialize_utils::cursor::{ read_bool, read_i64, read_option_u64, read_pubkey, read_pubkey_into, read_u32,