Skip to content

program-error!: Make ProgramError compatible with pinocchio #12

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 21 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
b016258
turn BorshIoError variants into unit variants
kevinheavey Feb 9, 2025
ce8e4f0
make std optional in solana-program-error
kevinheavey Feb 9, 2025
f618600
extract solana-pubkey-error crate
kevinheavey Feb 9, 2025
a240ce2
add missing feature activation
kevinheavey Feb 9, 2025
5d8fac0
extract instruction-error crate
kevinheavey Feb 9, 2025
b220f5d
remove unnecessary docs target from pubkey-error
kevinheavey Feb 9, 2025
9fd9cff
make num-traits optional in solana-program-error
kevinheavey Feb 9, 2025
9d71703
missing feature activation in solana-program
kevinheavey Feb 9, 2025
9337b27
move min_specialization to instruction-error
kevinheavey Feb 9, 2025
ee5318d
missing feature activation in solana-instruction
kevinheavey Feb 9, 2025
1cbc5e1
remove redundant DecodeError constraint from PrintProgramError
kevinheavey Feb 14, 2025
9300252
remove unncecessary Error constraint from PrintProgramError
kevinheavey Feb 14, 2025
eedffd0
remove num-traits dep and rename the num-traits feature to solana-msg
kevinheavey Feb 14, 2025
5d99ce2
add missing changes after removing num-traits
kevinheavey Feb 14, 2025
fa9fe91
make pubkey-error optional
kevinheavey Feb 15, 2025
dc30119
move instruction error codes to program-error and fix pubkey-error im…
kevinheavey Feb 15, 2025
904eb83
fmt
kevinheavey Feb 15, 2025
02c4d29
move PubkeyError back to solana-pubkey and move ProgramError conversi…
kevinheavey Mar 20, 2025
dbd235c
enable std if solana-msg is enabled
kevinheavey Mar 26, 2025
c4bdf17
add ToStr trait
kevinheavey Mar 26, 2025
d5cd36e
post-rebase fixes
kevinheavey Mar 29, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 14 additions & 7 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ members = [
"hash",
"inflation",
"instruction",
"instruction-error",
"instructions-sysvar",
"keccak-hasher",
"keypair",
Expand Down Expand Up @@ -246,6 +247,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.2.1", 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" }
Expand Down
37 changes: 37 additions & 0 deletions instruction-error/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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 }

[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 }

[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 = []

[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
all-features = true
rustdoc-args = ["--cfg=docsrs"]

[lints]
workspace = true
123 changes: 63 additions & 60 deletions instruction/src/error.rs → instruction-error/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,60 +1,31 @@
#![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.
/// Also, it is crucial that meta-information if any that comes along with
/// 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",
Expand Down Expand Up @@ -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,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirming that this change is good from the runtime side too -- the error text is never used or stored anywhere


/// An account does not have enough lamports to be rent-exempt
AccountNotRentExempt,
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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")
Expand All @@ -385,7 +347,7 @@ impl fmt::Display for InstructionError {
}
}

#[cfg(feature = "std")]
#[cfg(feature = "num-traits")]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I looked around to see if there was a standard feature name for num-traits. The highest downloaded crate that has it optional uses num-traits https://github.com/starkat99/half-rs, and the second just uses num https://github.com/jhpratt/deranged.

All that to say, I'm good with num-traits

impl<T> From<T> for InstructionError
where
T: ToPrimitive,
Expand All @@ -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,
Expand All @@ -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
Expand Down Expand Up @@ -453,7 +415,6 @@ impl fmt::Display for LamportsError {
}
}

#[cfg(feature = "std")]
impl From<LamportsError> for InstructionError {
fn from(error: LamportsError) -> Self {
match error {
Expand All @@ -462,3 +423,45 @@ impl From<LamportsError> for InstructionError {
}
}
}

impl TryFrom<InstructionError> for ProgramError {
type Error = InstructionError;

fn try_from(error: InstructionError) -> Result<Self, Self::Error> {
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),
}
}
}
14 changes: 4 additions & 10 deletions instruction/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,9 @@ edition = { workspace = true }
[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_os = "solana")'.dependencies]
Expand All @@ -34,18 +32,14 @@ solana-instruction = { path = ".", features = ["borsh"] }
bincode = ["dep:bincode", "dep:serde"]
borsh = ["dep:borsh"]
default = ["std"]
frozen-abi = [
"dep:solana-frozen-abi",
"dep:solana-frozen-abi-macro",
"serde",
"std",
]
frozen-abi = ["solana-instruction-error/frozen-abi"]
serde = [
"dep:serde",
"dep:serde_derive",
"solana-instruction-error/serde",
"solana-pubkey/serde",
]
std = []
std = ["solana-instruction-error/std"]

[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu", "wasm32-unknown-unknown"]
Expand Down
3 changes: 1 addition & 2 deletions instruction/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]

Expand All @@ -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(target_os = "solana")]
pub mod syscalls;
#[cfg(all(feature = "std", target_arch = "wasm32"))]
Expand Down
10 changes: 3 additions & 7 deletions program-error/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,15 @@ edition = { workspace = true }

[dependencies]
borsh = { workspace = true, optional = true }
num-traits = { workspace = true }
serde = { workspace = true, optional = true }
serde_derive = { workspace = true, optional = true }
solana-decode-error = { workspace = true }
solana-instruction = { workspace = true, default-features = false, features = [
"std",
] }
solana-msg = { workspace = true }
solana-pubkey = { workspace = true, default-features = false }
solana-msg = { workspace = true, optional = true }

[features]
borsh = ["dep:borsh"]
solana-msg = ["dep:solana-msg", "std"]
serde = ["dep:serde", "dep:serde_derive"]
std = []

[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
Expand Down
Loading