From 83b9a046d194e8ba30f1deb1f96b153366e7a271 Mon Sep 17 00:00:00 2001 From: Jack May Date: Thu, 3 Jun 2021 14:59:04 -0700 Subject: [PATCH] Add missing ProgramError to InstructionError mappings (#16231) * Add missing ProgramError to InstructionError mappings * add note * Clarify process of adding new program error --- programs/bpf_loader/src/lib.rs | 14 +++++- sdk/program/src/instruction.rs | 2 + sdk/program/src/program_error.rs | 81 ++++++++++++++++++-------------- sdk/src/feature_set.rs | 5 ++ 4 files changed, 64 insertions(+), 38 deletions(-) diff --git a/programs/bpf_loader/src/lib.rs b/programs/bpf_loader/src/lib.rs index 501f46658d..e74ffcc81b 100644 --- a/programs/bpf_loader/src/lib.rs +++ b/programs/bpf_loader/src/lib.rs @@ -31,13 +31,14 @@ use solana_sdk::{ bpf_loader_upgradeable::{self, UpgradeableLoaderState}, clock::Clock, entrypoint::SUCCESS, - feature_set::upgradeable_close_instruction, + feature_set::{add_missing_program_error_mappings, upgradeable_close_instruction}, ic_logger_msg, ic_msg, instruction::InstructionError, keyed_account::{from_keyed_account, keyed_account_at_index}, loader_instruction::LoaderInstruction, loader_upgradeable_instruction::UpgradeableLoaderInstruction, process_instruction::{stable_log, ComputeMeter, Executor, InvokeContext}, + program_error::{ACCOUNT_NOT_RENT_EXEMPT, BORSH_IO_ERROR}, program_utils::limited_deserialize, pubkey::Pubkey, rent::Rent, @@ -750,6 +751,8 @@ impl Executor for BpfExecutor { ) -> Result<(), InstructionError> { let logger = invoke_context.get_logger(); let invoke_depth = invoke_context.invoke_depth(); + let add_missing_program_error_mappings = + invoke_context.is_feature_active(&add_missing_program_error_mappings::id()); invoke_context.remove_first_keyed_account()?; @@ -803,7 +806,14 @@ impl Executor for BpfExecutor { match result { Ok(status) => { if status != SUCCESS { - let error: InstructionError = status.into(); + let error: InstructionError = if !add_missing_program_error_mappings + && (status == ACCOUNT_NOT_RENT_EXEMPT || status == BORSH_IO_ERROR) + { + // map originally missing error mappings to InvalidError + InstructionError::InvalidError + } else { + status.into() + }; stable_log::program_failure(&logger, program_id, &error); return Err(error); } diff --git a/sdk/program/src/instruction.rs b/sdk/program/src/instruction.rs index b6847c27d4..a158d1883f 100644 --- a/sdk/program/src/instruction.rs +++ b/sdk/program/src/instruction.rs @@ -215,6 +215,8 @@ pub enum InstructionError { /// Unsupported sysvar #[error("Unsupported sysvar")] UnsupportedSysvar, + // Note: For any new error added here an equivilent ProgramError and it's + // conversions must also be added } #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] diff --git a/sdk/program/src/program_error.rs b/sdk/program/src/program_error.rs index 7d7aa47180..b72543ec81 100644 --- a/sdk/program/src/program_error.rs +++ b/sdk/program/src/program_error.rs @@ -111,6 +111,11 @@ 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); +// Warning: Any new program errors added here must also be: +// - Added to the below conversions +// - Added as an equivilent to InstructionError +// - Be featureized in the BPF loader to return `InstructionError::InvalidError` +// until the feature is activated impl From for u64 { fn from(error: ProgramError) -> Self { @@ -131,7 +136,6 @@ impl From for u64 { ProgramError::BorshIoError(_) => BORSH_IO_ERROR, ProgramError::AccountNotRentExempt => ACCOUNT_NOT_RENT_EXEMPT, ProgramError::UnsupportedSysvar => UNSUPPORTED_SYSVAR, - ProgramError::Custom(error) => { if error == 0 { CUSTOM_ZERO @@ -146,22 +150,24 @@ impl From for u64 { impl From for ProgramError { fn from(error: u64) -> Self { match error { - INVALID_ARGUMENT => ProgramError::InvalidArgument, - INVALID_INSTRUCTION_DATA => ProgramError::InvalidInstructionData, - INVALID_ACCOUNT_DATA => ProgramError::InvalidAccountData, - ACCOUNT_DATA_TOO_SMALL => ProgramError::AccountDataTooSmall, - INSUFFICIENT_FUNDS => ProgramError::InsufficientFunds, - INCORRECT_PROGRAM_ID => ProgramError::IncorrectProgramId, - MISSING_REQUIRED_SIGNATURES => ProgramError::MissingRequiredSignature, - ACCOUNT_ALREADY_INITIALIZED => ProgramError::AccountAlreadyInitialized, - UNINITIALIZED_ACCOUNT => ProgramError::UninitializedAccount, - NOT_ENOUGH_ACCOUNT_KEYS => ProgramError::NotEnoughAccountKeys, - ACCOUNT_BORROW_FAILED => ProgramError::AccountBorrowFailed, - MAX_SEED_LENGTH_EXCEEDED => ProgramError::MaxSeedLengthExceeded, - INVALID_SEEDS => ProgramError::InvalidSeeds, - UNSUPPORTED_SYSVAR => ProgramError::UnsupportedSysvar, - CUSTOM_ZERO => ProgramError::Custom(0), - _ => ProgramError::Custom(error as u32), + CUSTOM_ZERO => Self::Custom(0), + INVALID_ARGUMENT => Self::InvalidArgument, + INVALID_INSTRUCTION_DATA => Self::InvalidInstructionData, + INVALID_ACCOUNT_DATA => Self::InvalidAccountData, + ACCOUNT_DATA_TOO_SMALL => Self::AccountDataTooSmall, + INSUFFICIENT_FUNDS => Self::InsufficientFunds, + INCORRECT_PROGRAM_ID => Self::IncorrectProgramId, + MISSING_REQUIRED_SIGNATURES => Self::MissingRequiredSignature, + ACCOUNT_ALREADY_INITIALIZED => Self::AccountAlreadyInitialized, + UNINITIALIZED_ACCOUNT => Self::UninitializedAccount, + NOT_ENOUGH_ACCOUNT_KEYS => Self::NotEnoughAccountKeys, + ACCOUNT_BORROW_FAILED => Self::AccountBorrowFailed, + MAX_SEED_LENGTH_EXCEEDED => Self::MaxSeedLengthExceeded, + INVALID_SEEDS => Self::InvalidSeeds, + BORSH_IO_ERROR => Self::BorshIoError("Unkown".to_string()), + ACCOUNT_NOT_RENT_EXEMPT => Self::AccountNotRentExempt, + UNSUPPORTED_SYSVAR => Self::UnsupportedSysvar, + _ => Self::Custom(error as u32), } } } @@ -184,6 +190,7 @@ impl TryFrom for ProgramError { 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), @@ -199,25 +206,27 @@ where fn from(error: T) -> Self { let error = error.to_u64().unwrap_or(0xbad_c0de); match error { - CUSTOM_ZERO => InstructionError::Custom(0), - INVALID_ARGUMENT => InstructionError::InvalidArgument, - INVALID_INSTRUCTION_DATA => InstructionError::InvalidInstructionData, - INVALID_ACCOUNT_DATA => InstructionError::InvalidAccountData, - ACCOUNT_DATA_TOO_SMALL => InstructionError::AccountDataTooSmall, - INSUFFICIENT_FUNDS => InstructionError::InsufficientFunds, - INCORRECT_PROGRAM_ID => InstructionError::IncorrectProgramId, - MISSING_REQUIRED_SIGNATURES => InstructionError::MissingRequiredSignature, - ACCOUNT_ALREADY_INITIALIZED => InstructionError::AccountAlreadyInitialized, - UNINITIALIZED_ACCOUNT => InstructionError::UninitializedAccount, - NOT_ENOUGH_ACCOUNT_KEYS => InstructionError::NotEnoughAccountKeys, - ACCOUNT_BORROW_FAILED => InstructionError::AccountBorrowFailed, - MAX_SEED_LENGTH_EXCEEDED => InstructionError::MaxSeedLengthExceeded, - INVALID_SEEDS => InstructionError::InvalidSeeds, - UNSUPPORTED_SYSVAR => InstructionError::UnsupportedSysvar, + CUSTOM_ZERO => Self::Custom(0), + INVALID_ARGUMENT => Self::InvalidArgument, + INVALID_INSTRUCTION_DATA => Self::InvalidInstructionData, + INVALID_ACCOUNT_DATA => Self::InvalidAccountData, + ACCOUNT_DATA_TOO_SMALL => Self::AccountDataTooSmall, + INSUFFICIENT_FUNDS => Self::InsufficientFunds, + INCORRECT_PROGRAM_ID => Self::IncorrectProgramId, + MISSING_REQUIRED_SIGNATURES => Self::MissingRequiredSignature, + ACCOUNT_ALREADY_INITIALIZED => Self::AccountAlreadyInitialized, + UNINITIALIZED_ACCOUNT => Self::UninitializedAccount, + NOT_ENOUGH_ACCOUNT_KEYS => Self::NotEnoughAccountKeys, + ACCOUNT_BORROW_FAILED => Self::AccountBorrowFailed, + MAX_SEED_LENGTH_EXCEEDED => Self::MaxSeedLengthExceeded, + INVALID_SEEDS => Self::InvalidSeeds, + BORSH_IO_ERROR => Self::BorshIoError("Unkown".to_string()), + ACCOUNT_NOT_RENT_EXEMPT => Self::AccountNotRentExempt, + UNSUPPORTED_SYSVAR => Self::UnsupportedSysvar, _ => { // A valid custom error has no bits set in the upper 32 if error >> BUILTIN_BIT_SHIFT == 0 { - InstructionError::Custom(error as u32) + Self::Custom(error as u32) } else { Self::InvalidError } @@ -229,14 +238,14 @@ where impl From for ProgramError { fn from(error: PubkeyError) -> Self { match error { - PubkeyError::MaxSeedLengthExceeded => ProgramError::MaxSeedLengthExceeded, - PubkeyError::InvalidSeeds => ProgramError::InvalidSeeds, + PubkeyError::MaxSeedLengthExceeded => Self::MaxSeedLengthExceeded, + PubkeyError::InvalidSeeds => Self::InvalidSeeds, } } } impl From for ProgramError { fn from(error: BorshIoError) -> Self { - ProgramError::BorshIoError(format!("{}", error)) + Self::BorshIoError(format!("{}", error)) } } diff --git a/sdk/src/feature_set.rs b/sdk/src/feature_set.rs index b401fc6856..377918eca6 100644 --- a/sdk/src/feature_set.rs +++ b/sdk/src/feature_set.rs @@ -139,6 +139,10 @@ pub mod memory_ops_syscalls { solana_sdk::declare_id!("ENQi37wsVhTvFz2gUiZAAbqFEWGN2jwFsqdEDTE8A4MU"); } +pub mod add_missing_program_error_mappings { + solana_sdk::declare_id!("3QEUpjhgPEt92nz3Mqf6pABkHPGCQwSvKtyGMq4SuQyL"); +} + lazy_static! { /// Map of feature identifiers to user-visible description pub static ref FEATURE_NAMES: HashMap = [ @@ -174,6 +178,7 @@ lazy_static! { (keccak256_syscall_enabled::id(), "keccak256 syscall"), (stake_program_v4::id(), "solana_stake_program v4"), (memory_ops_syscalls::id(), "add syscalls for memory operations"), + (add_missing_program_error_mappings::id(), "add missing program error mappings"), /*************** ADD NEW FEATURES HERE ***************/ ] .iter()