Add support for idiomatic error handling to BPF instruction processors (#7968)
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
extern crate solana_sdk;
|
||||
use solana_sdk::{
|
||||
account_info::AccountInfo, entrypoint, entrypoint::SUCCESS, info, pubkey::Pubkey,
|
||||
account_info::AccountInfo, entrypoint, info, program_error::ProgramError, pubkey::Pubkey,
|
||||
};
|
||||
|
||||
entrypoint!(process_instruction);
|
||||
@@ -10,9 +10,7 @@ fn process_instruction(
|
||||
_program_id: &Pubkey,
|
||||
accounts: &[AccountInfo],
|
||||
instruction_data: &[u8],
|
||||
) -> u32 {
|
||||
const FAILURE: u32 = 1;
|
||||
|
||||
) -> Result<(), ProgramError> {
|
||||
match instruction_data[0] {
|
||||
1 => {
|
||||
info!("modify first account data");
|
||||
@@ -45,8 +43,8 @@ fn process_instruction(
|
||||
}
|
||||
_ => {
|
||||
info!("Unrecognized command");
|
||||
return FAILURE;
|
||||
return Err(ProgramError::InvalidArgument);
|
||||
}
|
||||
}
|
||||
SUCCESS
|
||||
Ok(())
|
||||
}
|
||||
|
29
programs/bpf/rust/error_handling/Cargo.toml
Normal file
29
programs/bpf/rust/error_handling/Cargo.toml
Normal file
@@ -0,0 +1,29 @@
|
||||
|
||||
# Note: This crate must be built using do.sh
|
||||
|
||||
[package]
|
||||
name = "solana-bpf-rust-error-handling"
|
||||
version = "0.24.0"
|
||||
description = "Solana BPF test program written in Rust"
|
||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
num-derive = "0.2"
|
||||
num-traits = "0.2"
|
||||
solana-sdk = { path = "../../../../sdk/", version = "0.24.0", default-features = false }
|
||||
thiserror = "1.0"
|
||||
|
||||
[dev_dependencies]
|
||||
solana-sdk-bpf-test = { path = "../../../../sdk/bpf/rust/test", version = "0.24.0" }
|
||||
|
||||
[features]
|
||||
program = ["solana-sdk/program"]
|
||||
default = ["program"]
|
||||
|
||||
[lib]
|
||||
name = "solana_bpf_rust_error_handling"
|
||||
crate-type = ["cdylib"]
|
2
programs/bpf/rust/error_handling/Xargo.toml
Normal file
2
programs/bpf/rust/error_handling/Xargo.toml
Normal file
@@ -0,0 +1,2 @@
|
||||
[target.bpfel-unknown-unknown.dependencies.std]
|
||||
features = []
|
60
programs/bpf/rust/error_handling/src/lib.rs
Normal file
60
programs/bpf/rust/error_handling/src/lib.rs
Normal file
@@ -0,0 +1,60 @@
|
||||
//! @brief Example Rust-based BPF program that exercises error handling
|
||||
|
||||
extern crate solana_sdk;
|
||||
use num_derive::FromPrimitive;
|
||||
use solana_sdk::{
|
||||
account_info::AccountInfo, entrypoint, info, program_error::ProgramError, pubkey::Pubkey,
|
||||
};
|
||||
use thiserror::Error;
|
||||
|
||||
/// Custom program errors
|
||||
#[derive(Error, Debug, Clone, PartialEq, FromPrimitive)]
|
||||
// Clippy compains about 0x8000_002d, but we don't care about C compatibility here
|
||||
#[allow(clippy::enum_clike_unportable_variant)]
|
||||
pub enum MyError {
|
||||
#[error("The Answer")]
|
||||
TheAnswer = 42,
|
||||
#[error("Conflicting with success")]
|
||||
ConflictingSuccess = 0,
|
||||
#[error("Conflicting with builtin")]
|
||||
ConflictingBuiltin = 0x8000_002d,
|
||||
}
|
||||
impl From<MyError> for ProgramError {
|
||||
fn from(e: MyError) -> Self {
|
||||
ProgramError::CustomError(e as u32)
|
||||
}
|
||||
}
|
||||
|
||||
entrypoint!(process_instruction);
|
||||
fn process_instruction(
|
||||
_program_id: &Pubkey,
|
||||
_accounts: &[AccountInfo],
|
||||
instruction_data: &[u8],
|
||||
) -> Result<(), ProgramError> {
|
||||
match instruction_data[0] {
|
||||
1 => {
|
||||
info!("return success");
|
||||
Ok(())
|
||||
}
|
||||
2 => {
|
||||
info!("return a builtin");
|
||||
Err(ProgramError::AccountBorrowFailed)
|
||||
}
|
||||
3 => {
|
||||
info!("return custom error");
|
||||
Err(MyError::TheAnswer.into())
|
||||
}
|
||||
4 => {
|
||||
info!("return error that conflicts with success");
|
||||
Err(MyError::ConflictingSuccess.into())
|
||||
}
|
||||
5 => {
|
||||
info!("return error that conflicts with builtin");
|
||||
Err(MyError::ConflictingBuiltin.into())
|
||||
}
|
||||
_ => {
|
||||
info!("Unrecognized command");
|
||||
Err(ProgramError::InvalidInstructionData)
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,18 +1,20 @@
|
||||
//! @brief Example Rust-based BPF program that moves a lamport from one account to another
|
||||
|
||||
extern crate solana_sdk;
|
||||
use solana_sdk::{account_info::AccountInfo, entrypoint, entrypoint::SUCCESS, pubkey::Pubkey};
|
||||
use solana_sdk::{
|
||||
account_info::AccountInfo, entrypoint, program_error::ProgramError, pubkey::Pubkey,
|
||||
};
|
||||
|
||||
entrypoint!(process_instruction);
|
||||
fn process_instruction(
|
||||
_program_id: &Pubkey,
|
||||
accounts: &[AccountInfo],
|
||||
_instruction_data: &[u8],
|
||||
) -> u32 {
|
||||
) -> Result<(), ProgramError> {
|
||||
// account 0 is the mint and not owned by this program, any debit of its lamports
|
||||
// should result in a failed program execution. Test to ensure that this debit
|
||||
// is seen by the runtime and fails as expected
|
||||
**accounts[0].lamports.borrow_mut() -= 1;
|
||||
|
||||
SUCCESS
|
||||
Ok(())
|
||||
}
|
||||
|
@@ -3,8 +3,10 @@
|
||||
#![allow(unreachable_code)]
|
||||
|
||||
extern crate solana_sdk;
|
||||
|
||||
use solana_sdk::{
|
||||
account_info::AccountInfo, entrypoint, entrypoint::SUCCESS, info, log::*, pubkey::Pubkey,
|
||||
account_info::AccountInfo, entrypoint, info, log::*, program_error::ProgramError,
|
||||
pubkey::Pubkey,
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
@@ -24,7 +26,7 @@ fn process_instruction(
|
||||
program_id: &Pubkey,
|
||||
accounts: &[AccountInfo],
|
||||
instruction_data: &[u8],
|
||||
) -> u32 {
|
||||
) -> Result<(), ProgramError> {
|
||||
info!("Program identifier:");
|
||||
program_id.log();
|
||||
|
||||
@@ -58,7 +60,7 @@ fn process_instruction(
|
||||
panic!();
|
||||
}
|
||||
|
||||
SUCCESS
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@@ -4,10 +4,9 @@ extern crate solana_sdk;
|
||||
use solana_sdk::{
|
||||
account_info::AccountInfo,
|
||||
clock::{get_segment_from_slot, DEFAULT_SLOTS_PER_EPOCH, DEFAULT_SLOTS_PER_SEGMENT},
|
||||
entrypoint,
|
||||
entrypoint::SUCCESS,
|
||||
info,
|
||||
entrypoint, info,
|
||||
log::Log,
|
||||
program_error::ProgramError,
|
||||
pubkey::Pubkey,
|
||||
rent,
|
||||
sysvar::{
|
||||
@@ -21,7 +20,7 @@ fn process_instruction(
|
||||
_program_id: &Pubkey,
|
||||
accounts: &[AccountInfo],
|
||||
_instruction_data: &[u8],
|
||||
) -> u32 {
|
||||
) -> Result<(), ProgramError> {
|
||||
// Clock
|
||||
info!("Clock identifier:");
|
||||
sysvar::clock::id().log();
|
||||
@@ -66,5 +65,5 @@ fn process_instruction(
|
||||
(0, true)
|
||||
);
|
||||
|
||||
SUCCESS
|
||||
Ok(())
|
||||
}
|
||||
|
Reference in New Issue
Block a user