Remove program error footgun and cleaner developer experience (#8042)
This commit is contained in:
@ -6,7 +6,7 @@
|
||||
|
||||
#include <solana_sdk.h>
|
||||
|
||||
extern uint32_t entrypoint(const uint8_t *input) {
|
||||
extern uint64_t entrypoint(const uint8_t *input) {
|
||||
uint64_t x = *(uint64_t *) input;
|
||||
uint64_t *result = (uint64_t *) input + 1;
|
||||
uint64_t count = 0;
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
#include "helper.h"
|
||||
|
||||
extern uint32_t entrypoint(const uint8_t *input) {
|
||||
extern uint64_t entrypoint(const uint8_t *input) {
|
||||
sol_log(__FILE__);
|
||||
helper_function();
|
||||
sol_log(__FILE__);
|
||||
|
@ -8,12 +8,12 @@
|
||||
* Custom error for when input serialization fails
|
||||
*/
|
||||
|
||||
extern uint32_t entrypoint(const uint8_t *input) {
|
||||
extern uint64_t entrypoint(const uint8_t *input) {
|
||||
SolKeyedAccount ka[4];
|
||||
SolParameters params = (SolParameters) { .ka = ka };
|
||||
|
||||
if (!sol_deserialize(input, ¶ms, SOL_ARRAY_SIZE(ka))) {
|
||||
return INVALID_ARGUMENT;
|
||||
return ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
switch (params.data[0]) {
|
||||
@ -48,7 +48,7 @@ extern uint32_t entrypoint(const uint8_t *input) {
|
||||
break;
|
||||
default:
|
||||
sol_log("Unrecognized command");
|
||||
return INVALID_INSTRUCTION_DATA;
|
||||
return ERROR_INVALID_INSTRUCTION_DATA;
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
||||
|
@ -8,30 +8,33 @@
|
||||
* Custom error for when input serialization fails
|
||||
*/
|
||||
|
||||
extern uint32_t entrypoint(const uint8_t *input) {
|
||||
extern uint64_t entrypoint(const uint8_t *input) {
|
||||
SolKeyedAccount ka[4];
|
||||
SolParameters params = (SolParameters) { .ka = ka };
|
||||
|
||||
if (!sol_deserialize(input, ¶ms, SOL_ARRAY_SIZE(ka))) {
|
||||
return INVALID_ARGUMENT;
|
||||
return ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
switch (params.data[0]) {
|
||||
case(1):
|
||||
sol_log("return success");
|
||||
return SUCCESS;
|
||||
sol_log("return success");
|
||||
return SUCCESS;
|
||||
case(2):
|
||||
sol_log("return a builtin");
|
||||
return INVALID_ACCOUNT_DATA;
|
||||
sol_log("return a builtin");
|
||||
return ERROR_INVALID_ACCOUNT_DATA;
|
||||
case(3):
|
||||
sol_log("return custom error");
|
||||
return 42;
|
||||
sol_log("return custom error 42");
|
||||
return 42;
|
||||
case(4):
|
||||
sol_log("return error that conflicts with success");
|
||||
return 0x40000000;
|
||||
sol_log("return an invalid error");
|
||||
return ERROR_INVALID_ACCOUNT_DATA + 1;
|
||||
case(5):
|
||||
sol_log("return unknown builtin");
|
||||
return TO_BUILTIN(50);
|
||||
default:
|
||||
sol_log("Unrecognized command");
|
||||
return INVALID_INSTRUCTION_DATA;
|
||||
sol_log("Unrecognized command");
|
||||
return ERROR_INVALID_INSTRUCTION_DATA;
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
||||
|
@ -11,17 +11,17 @@
|
||||
*/
|
||||
#define NUM_KA 3
|
||||
|
||||
extern uint32_t entrypoint(const uint8_t *input) {
|
||||
extern uint64_t entrypoint(const uint8_t *input) {
|
||||
SolKeyedAccount ka[NUM_KA];
|
||||
SolParameters params = (SolParameters) { .ka = ka };
|
||||
|
||||
if (!sol_deserialize(input, ¶ms, SOL_ARRAY_SIZE(ka))) {
|
||||
return INVALID_ARGUMENT;
|
||||
return ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (!params.ka[0].is_signer) {
|
||||
sol_log("Transaction not signed by key 0");
|
||||
return MISSING_REQUIRED_SIGNATURES;
|
||||
return ERROR_MISSING_REQUIRED_SIGNATURES;
|
||||
}
|
||||
|
||||
int64_t lamports = *(int64_t *)params.data;
|
||||
|
@ -3,7 +3,7 @@
|
||||
static const char msg[] = "This is a message";
|
||||
static const char msg2[] = "This is a different message";
|
||||
|
||||
extern uint32_t entrypoint(const uint8_t *input) {
|
||||
extern uint64_t entrypoint(const uint8_t *input) {
|
||||
sol_log((char*)msg);
|
||||
sol_log((char*)msg2);
|
||||
return SUCCESS;
|
||||
|
@ -9,14 +9,14 @@
|
||||
*/
|
||||
#define INVALID_INPUT 1
|
||||
|
||||
extern uint32_t entrypoint(const uint8_t *input) {
|
||||
extern uint64_t entrypoint(const uint8_t *input) {
|
||||
SolKeyedAccount ka[1];
|
||||
SolParameters params = (SolParameters) { .ka = ka };
|
||||
|
||||
sol_log(__FILE__);
|
||||
|
||||
if (!sol_deserialize(input, ¶ms, SOL_ARRAY_SIZE(ka))) {
|
||||
return INVALID_ARGUMENT;
|
||||
return ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
// Log the provided input parameters. In the case of the no-op
|
||||
|
@ -4,14 +4,14 @@
|
||||
*/
|
||||
#include <solana_sdk.h>
|
||||
|
||||
extern uint32_t entrypoint(const uint8_t *input) {
|
||||
extern uint64_t entrypoint(const uint8_t *input) {
|
||||
SolKeyedAccount ka[1];
|
||||
SolParameters params = (SolParameters) { .ka = ka };
|
||||
|
||||
sol_log(__FILE__);
|
||||
|
||||
if (!sol_deserialize(input, ¶ms, SOL_ARRAY_SIZE(ka))) {
|
||||
return INVALID_ARGUMENT;
|
||||
return ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
// Log the provided input parameters. In the case of the no-op
|
||||
|
@ -4,7 +4,7 @@
|
||||
*/
|
||||
#include <solana_sdk.h>
|
||||
|
||||
extern uint32_t entrypoint(const uint8_t *input) {
|
||||
extern uint64_t entrypoint(const uint8_t *input) {
|
||||
sol_panic();
|
||||
return SUCCESS;
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ void __attribute__ ((noinline)) helper() {
|
||||
sol_log(__func__);
|
||||
}
|
||||
|
||||
extern uint32_t entrypoint(const uint8_t *input) {
|
||||
extern uint64_t entrypoint(const uint8_t *input) {
|
||||
sol_log(__func__);
|
||||
helper();
|
||||
return SUCCESS;
|
||||
|
@ -3,7 +3,7 @@
|
||||
struct foo {const uint8_t *input;};
|
||||
void foo(const uint8_t *input, struct foo foo) ;
|
||||
|
||||
extern uint32_t entrypoint(const uint8_t *input) {
|
||||
extern uint64_t entrypoint(const uint8_t *input) {
|
||||
struct foo f;
|
||||
f.input = input;
|
||||
foo(input, f);
|
||||
|
@ -15,7 +15,7 @@ static struct test_struct __attribute__ ((noinline)) test_function(void) {
|
||||
return s;
|
||||
}
|
||||
|
||||
extern uint32_t entrypoint(const uint8_t* input) {
|
||||
extern uint64_t entrypoint(const uint8_t* input) {
|
||||
struct test_struct s = test_function();
|
||||
sol_log("foobar");
|
||||
if (s.x + s.y + s.z == 12 ) {
|
||||
|
@ -4,7 +4,7 @@ extern crate solana_sdk;
|
||||
use solana_sdk::entrypoint::SUCCESS;
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn entrypoint(_input: *mut u8) -> u32 {
|
||||
pub extern "C" fn entrypoint(_input: *mut u8) -> u64 {
|
||||
let x: u128 = 1;
|
||||
let y = x.rotate_right(1);
|
||||
assert_eq!(y, 170_141_183_460_469_231_731_687_303_715_884_105_728);
|
||||
|
@ -7,7 +7,7 @@ use solana_sdk::{entrypoint::SUCCESS, info};
|
||||
use std::{alloc::Layout, mem};
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn entrypoint(_input: *mut u8) -> u32 {
|
||||
pub extern "C" fn entrypoint(_input: *mut u8) -> u64 {
|
||||
unsafe {
|
||||
// Confirm large allocation fails
|
||||
|
||||
|
@ -5,7 +5,7 @@ use byteorder::{ByteOrder, LittleEndian};
|
||||
use solana_sdk::entrypoint::SUCCESS;
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn entrypoint(_input: *mut u8) -> u32 {
|
||||
pub extern "C" fn entrypoint(_input: *mut u8) -> u64 {
|
||||
let mut buf = [0; 4];
|
||||
LittleEndian::write_u32(&mut buf, 1_000_000);
|
||||
assert_eq!(1_000_000, LittleEndian::read_u32(&buf));
|
||||
|
@ -9,15 +9,11 @@ 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("Default enum start")]
|
||||
DefaultEnumStart,
|
||||
#[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 {
|
||||
@ -41,16 +37,12 @@ fn process_instruction(
|
||||
Err(ProgramError::InvalidAccountData)
|
||||
}
|
||||
3 => {
|
||||
info!("return custom error");
|
||||
Err(MyError::TheAnswer.into())
|
||||
info!("return default enum start value");
|
||||
Err(MyError::DefaultEnumStart.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!("return custom error");
|
||||
Err(MyError::TheAnswer.into())
|
||||
}
|
||||
6 => {
|
||||
let data = accounts[0].try_borrow_mut_data()?;
|
||||
|
@ -4,7 +4,7 @@ extern crate solana_sdk;
|
||||
use solana_sdk::{entrypoint::SUCCESS, info};
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn entrypoint(_input: *mut u8) -> u32 {
|
||||
pub extern "C" fn entrypoint(_input: *mut u8) -> u64 {
|
||||
const ITERS: usize = 100;
|
||||
let ones = [1_u64; ITERS];
|
||||
let mut sum: u64 = 0;
|
||||
|
@ -5,7 +5,7 @@ extern crate solana_sdk;
|
||||
use solana_sdk::{entrypoint::SUCCESS, info};
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn entrypoint(_input: *mut u8) -> u32 {
|
||||
pub extern "C" fn entrypoint(_input: *mut u8) -> u64 {
|
||||
info!("Call same package");
|
||||
assert_eq!(crate::helper::many_args(1, 2, 3, 4, 5, 6, 7, 8, 9), 45);
|
||||
|
||||
|
@ -3,6 +3,6 @@
|
||||
extern crate solana_sdk;
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn entrypoint(_input: *mut u8) -> u32 {
|
||||
pub extern "C" fn entrypoint(_input: *mut u8) -> u64 {
|
||||
panic!();
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ use solana_bpf_rust_param_passing_dep::{Data, TestDep};
|
||||
use solana_sdk::{entrypoint::SUCCESS, info};
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn entrypoint(_input: *mut u8) -> u32 {
|
||||
pub extern "C" fn entrypoint(_input: *mut u8) -> u64 {
|
||||
let array = [0xA, 0xB, 0xC, 0xD, 0xE, 0xF];
|
||||
let data = Data {
|
||||
twentyone: 21u64,
|
||||
|
@ -196,7 +196,14 @@ mod bpf {
|
||||
let result = bank_client.send_instruction(&mint_keypair, instruction);
|
||||
assert_eq!(
|
||||
result.unwrap_err().unwrap(),
|
||||
TransactionError::InstructionError(0, InstructionError::ConflictingError(0))
|
||||
TransactionError::InstructionError(0, InstructionError::InvalidError)
|
||||
);
|
||||
|
||||
let instruction = Instruction::new(program_id, &5u8, account_metas.clone());
|
||||
let result = bank_client.send_instruction(&mint_keypair, instruction);
|
||||
assert_eq!(
|
||||
result.unwrap_err().unwrap(),
|
||||
TransactionError::InstructionError(0, InstructionError::InvalidError)
|
||||
);
|
||||
|
||||
let instruction = Instruction::new(program_id, &6u8, account_metas.clone());
|
||||
@ -391,24 +398,14 @@ mod bpf {
|
||||
let result = bank_client.send_instruction(&mint_keypair, instruction);
|
||||
assert_eq!(
|
||||
result.unwrap_err().unwrap(),
|
||||
TransactionError::InstructionError(0, InstructionError::CustomError(42))
|
||||
TransactionError::InstructionError(0, InstructionError::CustomError(0))
|
||||
);
|
||||
|
||||
let instruction = Instruction::new(program_id, &4u8, account_metas.clone());
|
||||
let result = bank_client.send_instruction(&mint_keypair, instruction);
|
||||
assert_eq!(
|
||||
result.unwrap_err().unwrap(),
|
||||
TransactionError::InstructionError(0, InstructionError::ConflictingError(0))
|
||||
);
|
||||
|
||||
let instruction = Instruction::new(program_id, &5u8, account_metas.clone());
|
||||
let result = bank_client.send_instruction(&mint_keypair, instruction);
|
||||
assert_eq!(
|
||||
result.unwrap_err().unwrap(),
|
||||
TransactionError::InstructionError(
|
||||
0,
|
||||
InstructionError::ConflictingError(0x8000_002d)
|
||||
)
|
||||
TransactionError::InstructionError(0, InstructionError::CustomError(42))
|
||||
);
|
||||
|
||||
let instruction = Instruction::new(program_id, &6u8, account_metas.clone());
|
||||
|
@ -8,6 +8,7 @@ use log::*;
|
||||
use solana_rbpf::{memory_region::MemoryRegion, EbpfVm};
|
||||
use solana_sdk::{
|
||||
account::KeyedAccount,
|
||||
entrypoint::SUCCESS,
|
||||
instruction::InstructionError,
|
||||
instruction_processor_utils::{is_executable, limited_deserialize, next_keyed_account},
|
||||
loader_instruction::LoaderInstruction,
|
||||
@ -145,9 +146,7 @@ pub fn process_instruction(
|
||||
info!("Call BPF program");
|
||||
match vm.execute_program(parameter_bytes.as_slice(), &[], &[heap_region]) {
|
||||
Ok(status) => {
|
||||
// ignore upper 32bits if any, programs only return lower 32bits
|
||||
let status = status as u32;
|
||||
if status != 0 {
|
||||
if status != SUCCESS {
|
||||
let error: InstructionError = status.into();
|
||||
warn!("BPF program failed: {:?}", error);
|
||||
return Err(error);
|
||||
|
Reference in New Issue
Block a user