Remove program error footgun and cleaner developer experience (#8042)
This commit is contained in:
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
#include <solana_sdk.h>
|
#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 x = *(uint64_t *) input;
|
||||||
uint64_t *result = (uint64_t *) input + 1;
|
uint64_t *result = (uint64_t *) input + 1;
|
||||||
uint64_t count = 0;
|
uint64_t count = 0;
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
#include "helper.h"
|
#include "helper.h"
|
||||||
|
|
||||||
extern uint32_t entrypoint(const uint8_t *input) {
|
extern uint64_t entrypoint(const uint8_t *input) {
|
||||||
sol_log(__FILE__);
|
sol_log(__FILE__);
|
||||||
helper_function();
|
helper_function();
|
||||||
sol_log(__FILE__);
|
sol_log(__FILE__);
|
||||||
|
@ -8,12 +8,12 @@
|
|||||||
* Custom error for when input serialization fails
|
* 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];
|
SolKeyedAccount ka[4];
|
||||||
SolParameters params = (SolParameters) { .ka = ka };
|
SolParameters params = (SolParameters) { .ka = ka };
|
||||||
|
|
||||||
if (!sol_deserialize(input, ¶ms, SOL_ARRAY_SIZE(ka))) {
|
if (!sol_deserialize(input, ¶ms, SOL_ARRAY_SIZE(ka))) {
|
||||||
return INVALID_ARGUMENT;
|
return ERROR_INVALID_ARGUMENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (params.data[0]) {
|
switch (params.data[0]) {
|
||||||
@ -48,7 +48,7 @@ extern uint32_t entrypoint(const uint8_t *input) {
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
sol_log("Unrecognized command");
|
sol_log("Unrecognized command");
|
||||||
return INVALID_INSTRUCTION_DATA;
|
return ERROR_INVALID_INSTRUCTION_DATA;
|
||||||
}
|
}
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -8,12 +8,12 @@
|
|||||||
* Custom error for when input serialization fails
|
* 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];
|
SolKeyedAccount ka[4];
|
||||||
SolParameters params = (SolParameters) { .ka = ka };
|
SolParameters params = (SolParameters) { .ka = ka };
|
||||||
|
|
||||||
if (!sol_deserialize(input, ¶ms, SOL_ARRAY_SIZE(ka))) {
|
if (!sol_deserialize(input, ¶ms, SOL_ARRAY_SIZE(ka))) {
|
||||||
return INVALID_ARGUMENT;
|
return ERROR_INVALID_ARGUMENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (params.data[0]) {
|
switch (params.data[0]) {
|
||||||
@ -22,16 +22,19 @@ extern uint32_t entrypoint(const uint8_t *input) {
|
|||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
case(2):
|
case(2):
|
||||||
sol_log("return a builtin");
|
sol_log("return a builtin");
|
||||||
return INVALID_ACCOUNT_DATA;
|
return ERROR_INVALID_ACCOUNT_DATA;
|
||||||
case(3):
|
case(3):
|
||||||
sol_log("return custom error");
|
sol_log("return custom error 42");
|
||||||
return 42;
|
return 42;
|
||||||
case(4):
|
case(4):
|
||||||
sol_log("return error that conflicts with success");
|
sol_log("return an invalid error");
|
||||||
return 0x40000000;
|
return ERROR_INVALID_ACCOUNT_DATA + 1;
|
||||||
|
case(5):
|
||||||
|
sol_log("return unknown builtin");
|
||||||
|
return TO_BUILTIN(50);
|
||||||
default:
|
default:
|
||||||
sol_log("Unrecognized command");
|
sol_log("Unrecognized command");
|
||||||
return INVALID_INSTRUCTION_DATA;
|
return ERROR_INVALID_INSTRUCTION_DATA;
|
||||||
}
|
}
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -11,17 +11,17 @@
|
|||||||
*/
|
*/
|
||||||
#define NUM_KA 3
|
#define NUM_KA 3
|
||||||
|
|
||||||
extern uint32_t entrypoint(const uint8_t *input) {
|
extern uint64_t entrypoint(const uint8_t *input) {
|
||||||
SolKeyedAccount ka[NUM_KA];
|
SolKeyedAccount ka[NUM_KA];
|
||||||
SolParameters params = (SolParameters) { .ka = ka };
|
SolParameters params = (SolParameters) { .ka = ka };
|
||||||
|
|
||||||
if (!sol_deserialize(input, ¶ms, SOL_ARRAY_SIZE(ka))) {
|
if (!sol_deserialize(input, ¶ms, SOL_ARRAY_SIZE(ka))) {
|
||||||
return INVALID_ARGUMENT;
|
return ERROR_INVALID_ARGUMENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!params.ka[0].is_signer) {
|
if (!params.ka[0].is_signer) {
|
||||||
sol_log("Transaction not signed by key 0");
|
sol_log("Transaction not signed by key 0");
|
||||||
return MISSING_REQUIRED_SIGNATURES;
|
return ERROR_MISSING_REQUIRED_SIGNATURES;
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t lamports = *(int64_t *)params.data;
|
int64_t lamports = *(int64_t *)params.data;
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
static const char msg[] = "This is a message";
|
static const char msg[] = "This is a message";
|
||||||
static const char msg2[] = "This is a different 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*)msg);
|
||||||
sol_log((char*)msg2);
|
sol_log((char*)msg2);
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
|
@ -9,14 +9,14 @@
|
|||||||
*/
|
*/
|
||||||
#define INVALID_INPUT 1
|
#define INVALID_INPUT 1
|
||||||
|
|
||||||
extern uint32_t entrypoint(const uint8_t *input) {
|
extern uint64_t entrypoint(const uint8_t *input) {
|
||||||
SolKeyedAccount ka[1];
|
SolKeyedAccount ka[1];
|
||||||
SolParameters params = (SolParameters) { .ka = ka };
|
SolParameters params = (SolParameters) { .ka = ka };
|
||||||
|
|
||||||
sol_log(__FILE__);
|
sol_log(__FILE__);
|
||||||
|
|
||||||
if (!sol_deserialize(input, ¶ms, SOL_ARRAY_SIZE(ka))) {
|
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
|
// Log the provided input parameters. In the case of the no-op
|
||||||
|
@ -4,14 +4,14 @@
|
|||||||
*/
|
*/
|
||||||
#include <solana_sdk.h>
|
#include <solana_sdk.h>
|
||||||
|
|
||||||
extern uint32_t entrypoint(const uint8_t *input) {
|
extern uint64_t entrypoint(const uint8_t *input) {
|
||||||
SolKeyedAccount ka[1];
|
SolKeyedAccount ka[1];
|
||||||
SolParameters params = (SolParameters) { .ka = ka };
|
SolParameters params = (SolParameters) { .ka = ka };
|
||||||
|
|
||||||
sol_log(__FILE__);
|
sol_log(__FILE__);
|
||||||
|
|
||||||
if (!sol_deserialize(input, ¶ms, SOL_ARRAY_SIZE(ka))) {
|
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
|
// Log the provided input parameters. In the case of the no-op
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
#include <solana_sdk.h>
|
#include <solana_sdk.h>
|
||||||
|
|
||||||
extern uint32_t entrypoint(const uint8_t *input) {
|
extern uint64_t entrypoint(const uint8_t *input) {
|
||||||
sol_panic();
|
sol_panic();
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ void __attribute__ ((noinline)) helper() {
|
|||||||
sol_log(__func__);
|
sol_log(__func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern uint32_t entrypoint(const uint8_t *input) {
|
extern uint64_t entrypoint(const uint8_t *input) {
|
||||||
sol_log(__func__);
|
sol_log(__func__);
|
||||||
helper();
|
helper();
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
struct foo {const uint8_t *input;};
|
struct foo {const uint8_t *input;};
|
||||||
void foo(const uint8_t *input, struct foo foo) ;
|
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;
|
struct foo f;
|
||||||
f.input = input;
|
f.input = input;
|
||||||
foo(input, f);
|
foo(input, f);
|
||||||
|
@ -15,7 +15,7 @@ static struct test_struct __attribute__ ((noinline)) test_function(void) {
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern uint32_t entrypoint(const uint8_t* input) {
|
extern uint64_t entrypoint(const uint8_t* input) {
|
||||||
struct test_struct s = test_function();
|
struct test_struct s = test_function();
|
||||||
sol_log("foobar");
|
sol_log("foobar");
|
||||||
if (s.x + s.y + s.z == 12 ) {
|
if (s.x + s.y + s.z == 12 ) {
|
||||||
|
@ -4,7 +4,7 @@ extern crate solana_sdk;
|
|||||||
use solana_sdk::entrypoint::SUCCESS;
|
use solana_sdk::entrypoint::SUCCESS;
|
||||||
|
|
||||||
#[no_mangle]
|
#[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 x: u128 = 1;
|
||||||
let y = x.rotate_right(1);
|
let y = x.rotate_right(1);
|
||||||
assert_eq!(y, 170_141_183_460_469_231_731_687_303_715_884_105_728);
|
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};
|
use std::{alloc::Layout, mem};
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn entrypoint(_input: *mut u8) -> u32 {
|
pub extern "C" fn entrypoint(_input: *mut u8) -> u64 {
|
||||||
unsafe {
|
unsafe {
|
||||||
// Confirm large allocation fails
|
// Confirm large allocation fails
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ use byteorder::{ByteOrder, LittleEndian};
|
|||||||
use solana_sdk::entrypoint::SUCCESS;
|
use solana_sdk::entrypoint::SUCCESS;
|
||||||
|
|
||||||
#[no_mangle]
|
#[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];
|
let mut buf = [0; 4];
|
||||||
LittleEndian::write_u32(&mut buf, 1_000_000);
|
LittleEndian::write_u32(&mut buf, 1_000_000);
|
||||||
assert_eq!(1_000_000, LittleEndian::read_u32(&buf));
|
assert_eq!(1_000_000, LittleEndian::read_u32(&buf));
|
||||||
|
@ -9,15 +9,11 @@ use thiserror::Error;
|
|||||||
|
|
||||||
/// Custom program errors
|
/// Custom program errors
|
||||||
#[derive(Error, Debug, Clone, PartialEq, FromPrimitive)]
|
#[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 {
|
pub enum MyError {
|
||||||
|
#[error("Default enum start")]
|
||||||
|
DefaultEnumStart,
|
||||||
#[error("The Answer")]
|
#[error("The Answer")]
|
||||||
TheAnswer = 42,
|
TheAnswer = 42,
|
||||||
#[error("Conflicting with success")]
|
|
||||||
ConflictingSuccess = 0,
|
|
||||||
#[error("Conflicting with builtin")]
|
|
||||||
ConflictingBuiltin = 0x8000_002d,
|
|
||||||
}
|
}
|
||||||
impl From<MyError> for ProgramError {
|
impl From<MyError> for ProgramError {
|
||||||
fn from(e: MyError) -> Self {
|
fn from(e: MyError) -> Self {
|
||||||
@ -41,16 +37,12 @@ fn process_instruction(
|
|||||||
Err(ProgramError::InvalidAccountData)
|
Err(ProgramError::InvalidAccountData)
|
||||||
}
|
}
|
||||||
3 => {
|
3 => {
|
||||||
info!("return custom error");
|
info!("return default enum start value");
|
||||||
Err(MyError::TheAnswer.into())
|
Err(MyError::DefaultEnumStart.into())
|
||||||
}
|
}
|
||||||
4 => {
|
4 => {
|
||||||
info!("return error that conflicts with success");
|
info!("return custom error");
|
||||||
Err(MyError::ConflictingSuccess.into())
|
Err(MyError::TheAnswer.into())
|
||||||
}
|
|
||||||
5 => {
|
|
||||||
info!("return error that conflicts with builtin");
|
|
||||||
Err(MyError::ConflictingBuiltin.into())
|
|
||||||
}
|
}
|
||||||
6 => {
|
6 => {
|
||||||
let data = accounts[0].try_borrow_mut_data()?;
|
let data = accounts[0].try_borrow_mut_data()?;
|
||||||
|
@ -4,7 +4,7 @@ extern crate solana_sdk;
|
|||||||
use solana_sdk::{entrypoint::SUCCESS, info};
|
use solana_sdk::{entrypoint::SUCCESS, info};
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn entrypoint(_input: *mut u8) -> u32 {
|
pub extern "C" fn entrypoint(_input: *mut u8) -> u64 {
|
||||||
const ITERS: usize = 100;
|
const ITERS: usize = 100;
|
||||||
let ones = [1_u64; ITERS];
|
let ones = [1_u64; ITERS];
|
||||||
let mut sum: u64 = 0;
|
let mut sum: u64 = 0;
|
||||||
|
@ -5,7 +5,7 @@ extern crate solana_sdk;
|
|||||||
use solana_sdk::{entrypoint::SUCCESS, info};
|
use solana_sdk::{entrypoint::SUCCESS, info};
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn entrypoint(_input: *mut u8) -> u32 {
|
pub extern "C" fn entrypoint(_input: *mut u8) -> u64 {
|
||||||
info!("Call same package");
|
info!("Call same package");
|
||||||
assert_eq!(crate::helper::many_args(1, 2, 3, 4, 5, 6, 7, 8, 9), 45);
|
assert_eq!(crate::helper::many_args(1, 2, 3, 4, 5, 6, 7, 8, 9), 45);
|
||||||
|
|
||||||
|
@ -3,6 +3,6 @@
|
|||||||
extern crate solana_sdk;
|
extern crate solana_sdk;
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn entrypoint(_input: *mut u8) -> u32 {
|
pub extern "C" fn entrypoint(_input: *mut u8) -> u64 {
|
||||||
panic!();
|
panic!();
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ use solana_bpf_rust_param_passing_dep::{Data, TestDep};
|
|||||||
use solana_sdk::{entrypoint::SUCCESS, info};
|
use solana_sdk::{entrypoint::SUCCESS, info};
|
||||||
|
|
||||||
#[no_mangle]
|
#[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 array = [0xA, 0xB, 0xC, 0xD, 0xE, 0xF];
|
||||||
let data = Data {
|
let data = Data {
|
||||||
twentyone: 21u64,
|
twentyone: 21u64,
|
||||||
|
@ -196,7 +196,14 @@ mod bpf {
|
|||||||
let result = bank_client.send_instruction(&mint_keypair, instruction);
|
let result = bank_client.send_instruction(&mint_keypair, instruction);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result.unwrap_err().unwrap(),
|
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());
|
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);
|
let result = bank_client.send_instruction(&mint_keypair, instruction);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result.unwrap_err().unwrap(),
|
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 instruction = Instruction::new(program_id, &4u8, account_metas.clone());
|
||||||
let result = bank_client.send_instruction(&mint_keypair, instruction);
|
let result = bank_client.send_instruction(&mint_keypair, instruction);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result.unwrap_err().unwrap(),
|
result.unwrap_err().unwrap(),
|
||||||
TransactionError::InstructionError(0, InstructionError::ConflictingError(0))
|
TransactionError::InstructionError(0, InstructionError::CustomError(42))
|
||||||
);
|
|
||||||
|
|
||||||
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)
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let instruction = Instruction::new(program_id, &6u8, account_metas.clone());
|
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_rbpf::{memory_region::MemoryRegion, EbpfVm};
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account::KeyedAccount,
|
account::KeyedAccount,
|
||||||
|
entrypoint::SUCCESS,
|
||||||
instruction::InstructionError,
|
instruction::InstructionError,
|
||||||
instruction_processor_utils::{is_executable, limited_deserialize, next_keyed_account},
|
instruction_processor_utils::{is_executable, limited_deserialize, next_keyed_account},
|
||||||
loader_instruction::LoaderInstruction,
|
loader_instruction::LoaderInstruction,
|
||||||
@ -145,9 +146,7 @@ pub fn process_instruction(
|
|||||||
info!("Call BPF program");
|
info!("Call BPF program");
|
||||||
match vm.execute_program(parameter_bytes.as_slice(), &[], &[heap_region]) {
|
match vm.execute_program(parameter_bytes.as_slice(), &[], &[heap_region]) {
|
||||||
Ok(status) => {
|
Ok(status) => {
|
||||||
// ignore upper 32bits if any, programs only return lower 32bits
|
if status != SUCCESS {
|
||||||
let status = status as u32;
|
|
||||||
if status != 0 {
|
|
||||||
let error: InstructionError = status.into();
|
let error: InstructionError = status.into();
|
||||||
warn!("BPF program failed: {:?}", error);
|
warn!("BPF program failed: {:?}", error);
|
||||||
return Err(error);
|
return Err(error);
|
||||||
|
@ -51,40 +51,40 @@ static_assert(sizeof(uint64_t) == 8);
|
|||||||
*/
|
*/
|
||||||
#define NULL 0
|
#define NULL 0
|
||||||
|
|
||||||
/**
|
/** Indicates the instruction was processed successfully */
|
||||||
* SUCCESS return value
|
|
||||||
*/
|
|
||||||
#define SUCCESS 0
|
#define SUCCESS 0
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builtin program error return values have the 31st bit set. Programs
|
* Builtin program status values occupy the upper 32 bits of the program return
|
||||||
* may define their own values but their 31st and 30th bit must be unset
|
* value. Programs may define their own error values but they must be confined
|
||||||
* to avoid conflicting with the builtin errors
|
* to the lower 32 bits.
|
||||||
*/
|
*/
|
||||||
#define BUILTIN_ERROR_START 0x80000000
|
#define TO_BUILTIN(error) ((uint64_t)(error) << 32)
|
||||||
|
|
||||||
/** The arguments provided to a program instruction where invalid */
|
|
||||||
#define INVALID_ARGUMENT (BUILTIN_ERROR_START + 0)
|
|
||||||
/** An instruction's data contents was invalid */
|
|
||||||
#define INVALID_INSTRUCTION_DATA (BUILTIN_ERROR_START + 1)
|
|
||||||
/** An account's data contents was invalid */
|
|
||||||
#define INVALID_ACCOUNT_DATA (BUILTIN_ERROR_START + 2)
|
|
||||||
/** An account's data was too small */
|
|
||||||
#define ACCOUNT_DATA_TOO_SMALL (BUILTIN_ERROR_START + 3)
|
|
||||||
/** An account's balance was too small to complete the instruction */
|
|
||||||
#define INSUFFICIENT_FUNDS (BUILTIN_ERROR_START + 4)
|
|
||||||
/** The account did not have the expected program id */
|
|
||||||
#define INCORRECT_PROGRAM_ID (BUILTIN_ERROR_START + 5)
|
|
||||||
/** A signature was required but not found */
|
|
||||||
#define MISSING_REQUIRED_SIGNATURES (BUILTIN_ERROR_START + 6)
|
|
||||||
/** An initialize instruction was sent to an account that has already been initialized */
|
|
||||||
#define ACCOUNT_ALREADY_INITIALIZED (BUILTIN_ERROR_START + 7)
|
|
||||||
/** An attempt to operate on an account that hasn't been initialized */
|
|
||||||
#define UNINITIALIZED_ACCOUNT (BUILTIN_ERROR_START + 8)
|
|
||||||
/** The instruction expected additional account keys */
|
|
||||||
#define NOT_ENOUGH_ACCOUNT_KEYS (BUILTIN_ERROR_START + 9)
|
|
||||||
/** Note: Not applicable to program written in C */
|
/** Note: Not applicable to program written in C */
|
||||||
#define ACCOUNT_BORROW_FAILED (BUILTIN_ERROR_START + 10)
|
#define ERROR_CUSTOM_ZERO TO_BUILTIN(1)
|
||||||
|
/** The arguments provided to a program instruction where invalid */
|
||||||
|
#define ERROR_INVALID_ARGUMENT TO_BUILTIN(2)
|
||||||
|
/** An instruction's data contents was invalid */
|
||||||
|
#define ERROR_INVALID_INSTRUCTION_DATA TO_BUILTIN(3)
|
||||||
|
/** An account's data contents was invalid */
|
||||||
|
#define ERROR_INVALID_ACCOUNT_DATA TO_BUILTIN(4)
|
||||||
|
/** An account's data was too small */
|
||||||
|
#define ERROR_ACCOUNT_DATA_TOO_SMALL TO_BUILTIN(5)
|
||||||
|
/** An account's balance was too small to complete the instruction */
|
||||||
|
#define ERROR_INSUFFICIENT_FUNDS TO_BUILTIN(6)
|
||||||
|
/** The account did not have the expected program id */
|
||||||
|
#define ERROR_INCORRECT_PROGRAM_ID TO_BUILTIN(7)
|
||||||
|
/** A signature was required but not found */
|
||||||
|
#define ERROR_MISSING_REQUIRED_SIGNATURES TO_BUILTIN(8)
|
||||||
|
/** An initialize instruction was sent to an account that has already been initialized */
|
||||||
|
#define ERROR_ACCOUNT_ALREADY_INITIALIZED TO_BUILTIN(9)
|
||||||
|
/** An attempt to operate on an account that hasn't been initialized */
|
||||||
|
#define ERROR_UNINITIALIZED_ACCOUNT TO_BUILTIN(10)
|
||||||
|
/** The instruction expected additional account keys */
|
||||||
|
#define ERROR_NOT_ENOUGH_ACCOUNT_KEYS TO_BUILTIN(11)
|
||||||
|
/** Note: Not applicable to program written in C */
|
||||||
|
#define ERROR_ACCOUNT_BORROW_FAILED TO_BUILTIN(12)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Boolean type
|
* Boolean type
|
||||||
@ -367,7 +367,7 @@ SOL_FN_PREFIX void sol_log_params(const SolParameters *params) {
|
|||||||
* @param input Buffer of serialized input parameters. Use sol_deserialize() to decode
|
* @param input Buffer of serialized input parameters. Use sol_deserialize() to decode
|
||||||
* @return 0 if the instruction executed successfully
|
* @return 0 if the instruction executed successfully
|
||||||
*/
|
*/
|
||||||
uint32_t entrypoint(const uint8_t *input);
|
uint64_t entrypoint(const uint8_t *input);
|
||||||
|
|
||||||
|
|
||||||
#ifdef SOL_TEST
|
#ifdef SOL_TEST
|
||||||
|
@ -22,12 +22,12 @@ pub type ProcessInstruction = fn(
|
|||||||
) -> Result<(), ProgramError>;
|
) -> Result<(), ProgramError>;
|
||||||
|
|
||||||
/// Programs indicate success with a return value of 0
|
/// Programs indicate success with a return value of 0
|
||||||
pub const SUCCESS: u32 = 0;
|
pub const SUCCESS: u64 = 0;
|
||||||
|
|
||||||
/// Declare the entry point of the program.
|
/// Declare the entry point of the program.
|
||||||
///
|
///
|
||||||
/// Deserialize the program input arguments and call
|
/// Deserialize the program input arguments and call
|
||||||
/// the user defined `ProcessInstruction` function.
|
/// the user defined `process_instruction` function.
|
||||||
/// Users must call this macro otherwise an entry point for
|
/// Users must call this macro otherwise an entry point for
|
||||||
/// their program will not be created.
|
/// their program will not be created.
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
@ -35,7 +35,7 @@ macro_rules! entrypoint {
|
|||||||
($process_instruction:ident) => {
|
($process_instruction:ident) => {
|
||||||
/// # Safety
|
/// # Safety
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u32 {
|
pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u64 {
|
||||||
let (program_id, accounts, instruction_data) =
|
let (program_id, accounts, instruction_data) =
|
||||||
unsafe { $crate::entrypoint::deserialize(input) };
|
unsafe { $crate::entrypoint::deserialize(input) };
|
||||||
match $process_instruction(&program_id, &accounts, &instruction_data) {
|
match $process_instruction(&program_id, &accounts, &instruction_data) {
|
||||||
|
@ -86,18 +86,14 @@ pub enum InstructionError {
|
|||||||
/// the runtime cannot determine which changes to pick or how to merge them if both are modified
|
/// the runtime cannot determine which changes to pick or how to merge them if both are modified
|
||||||
DuplicateAccountOutOfSync,
|
DuplicateAccountOutOfSync,
|
||||||
|
|
||||||
/// CustomError allows on-chain programs to implement program-specific error types and see
|
/// The return value from the program was invalid. Valid errors are either a defined builtin
|
||||||
/// them returned by the Solana runtime. A CustomError may be any type that is represented
|
/// error value or a user-defined error in the lower 32 bits.
|
||||||
/// as or serialized to a u32 integer.
|
InvalidError,
|
||||||
///
|
|
||||||
/// NOTE: u64 requires special serialization to avoid the loss of precision in JS clients and
|
|
||||||
/// so is not used for now.
|
|
||||||
CustomError(u32),
|
|
||||||
|
|
||||||
/// Like CustomError but the return value from the program conflicted with
|
/// Allows on-chain programs to implement program-specific error types and see them returned
|
||||||
/// a builtin error. The value held by this variant is the u32 error code
|
/// by the Solana runtime. A program-specific error may be any type that is represented as
|
||||||
/// returned by the program but with the 30th bit cleared.
|
/// or serialized to a u32 integer.
|
||||||
ConflictingError(u32),
|
CustomError(u32),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InstructionError {
|
impl InstructionError {
|
||||||
|
@ -3,12 +3,9 @@ use num_traits::ToPrimitive;
|
|||||||
|
|
||||||
/// Reasons the program may fail
|
/// Reasons the program may fail
|
||||||
pub enum ProgramError {
|
pub enum ProgramError {
|
||||||
/// CustomError allows programs to implement program-specific error types and see
|
/// Allows on-chain programs to implement program-specific error types and see them returned
|
||||||
/// them returned by the Solana runtime. A CustomError may be any type that is represented
|
/// by the Solana runtime. A program-specific error may be any type that is represented as
|
||||||
/// as or serialized to a u32 integer.
|
/// or serialized to a u32 integer.
|
||||||
///
|
|
||||||
/// NOTE: u64 requires special serialization to avoid the loss of precision in JS clients and
|
|
||||||
/// so is not used for now.
|
|
||||||
CustomError(u32),
|
CustomError(u32),
|
||||||
/// The arguments provided to a program instruction where invalid
|
/// The arguments provided to a program instruction where invalid
|
||||||
InvalidArgument,
|
InvalidArgument,
|
||||||
@ -34,47 +31,28 @@ pub enum ProgramError {
|
|||||||
AccountBorrowFailed,
|
AccountBorrowFailed,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 32bit representations of builtin program errors returned by the entry point
|
/// Builtin return values occupy the upper 32 bits
|
||||||
const BUILTIN_ERROR_START: u32 = 0x8000_0000; // 31st bit set
|
const BUILTIN_BIT_SHIFT: usize = 32;
|
||||||
const INVALID_ARGUMENT: u32 = BUILTIN_ERROR_START;
|
macro_rules! to_builtin {
|
||||||
const INVALID_INSTRUCTION_DATA: u32 = BUILTIN_ERROR_START + 1;
|
($error:expr) => {
|
||||||
const INVALID_ACCOUNT_DATA: u32 = BUILTIN_ERROR_START + 2;
|
($error as u64) << BUILTIN_BIT_SHIFT
|
||||||
const ACCOUNT_DATA_TOO_SMALL: u32 = BUILTIN_ERROR_START + 3;
|
};
|
||||||
const INSUFFICIENT_FUNDS: u32 = BUILTIN_ERROR_START + 4;
|
|
||||||
const INCORRECT_PROGRAM_ID: u32 = BUILTIN_ERROR_START + 5;
|
|
||||||
const MISSING_REQUIRED_SIGNATURES: u32 = BUILTIN_ERROR_START + 6;
|
|
||||||
const ACCOUNT_ALREADY_INITIALIZED: u32 = BUILTIN_ERROR_START + 7;
|
|
||||||
const UNINITIALIZED_ACCOUNT: u32 = BUILTIN_ERROR_START + 8;
|
|
||||||
const NOT_ENOUGH_ACCOUNT_KEYS: u32 = BUILTIN_ERROR_START + 9;
|
|
||||||
const ACCOUNT_BORROW_FAILED: u32 = BUILTIN_ERROR_START + 10;
|
|
||||||
|
|
||||||
/// Is this a builtin error? (is 31th bit set?)
|
|
||||||
fn is_builtin(error: u32) -> bool {
|
|
||||||
(error & BUILTIN_ERROR_START) != 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If a program defined error conflicts with a builtin error
|
const CUSTOM_ZERO: u64 = to_builtin!(1);
|
||||||
/// its 30th bit is set before returning to distinguish it.
|
const INVALID_ARGUMENT: u64 = to_builtin!(2);
|
||||||
/// The side effect is that the original error's 30th bit
|
const INVALID_INSTRUCTION_DATA: u64 = to_builtin!(3);
|
||||||
/// value is lost, be aware.
|
const INVALID_ACCOUNT_DATA: u64 = to_builtin!(4);
|
||||||
const CONFLICTING_ERROR_MARK: u32 = 0x4000_0000; // 30st bit set
|
const ACCOUNT_DATA_TOO_SMALL: u64 = to_builtin!(5);
|
||||||
|
const INSUFFICIENT_FUNDS: u64 = to_builtin!(6);
|
||||||
|
const INCORRECT_PROGRAM_ID: u64 = to_builtin!(7);
|
||||||
|
const MISSING_REQUIRED_SIGNATURES: u64 = to_builtin!(8);
|
||||||
|
const ACCOUNT_ALREADY_INITIALIZED: u64 = to_builtin!(9);
|
||||||
|
const UNINITIALIZED_ACCOUNT: u64 = to_builtin!(10);
|
||||||
|
const NOT_ENOUGH_ACCOUNT_KEYS: u64 = to_builtin!(11);
|
||||||
|
const ACCOUNT_BORROW_FAILED: u64 = to_builtin!(12);
|
||||||
|
|
||||||
/// Is this error marked as conflicting? (is 30th bit set?)
|
impl From<ProgramError> for u64 {
|
||||||
fn is_marked_conflicting(error: u32) -> bool {
|
|
||||||
(error & CONFLICTING_ERROR_MARK) != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Mark as a conflicting error
|
|
||||||
fn mark_conflicting(error: u32) -> u32 {
|
|
||||||
error | CONFLICTING_ERROR_MARK
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Unmark as a conflicting error
|
|
||||||
fn unmark_conflicting(error: u32) -> u32 {
|
|
||||||
error & !CONFLICTING_ERROR_MARK
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ProgramError> for u32 {
|
|
||||||
fn from(error: ProgramError) -> Self {
|
fn from(error: ProgramError) -> Self {
|
||||||
match error {
|
match error {
|
||||||
ProgramError::InvalidArgument => INVALID_ARGUMENT,
|
ProgramError::InvalidArgument => INVALID_ARGUMENT,
|
||||||
@ -89,10 +67,10 @@ impl From<ProgramError> for u32 {
|
|||||||
ProgramError::NotEnoughAccountKeys => NOT_ENOUGH_ACCOUNT_KEYS,
|
ProgramError::NotEnoughAccountKeys => NOT_ENOUGH_ACCOUNT_KEYS,
|
||||||
ProgramError::AccountBorrowFailed => ACCOUNT_BORROW_FAILED,
|
ProgramError::AccountBorrowFailed => ACCOUNT_BORROW_FAILED,
|
||||||
ProgramError::CustomError(error) => {
|
ProgramError::CustomError(error) => {
|
||||||
if error == 0 || is_builtin(error) {
|
if error == 0 {
|
||||||
mark_conflicting(error)
|
CUSTOM_ZERO
|
||||||
} else {
|
} else {
|
||||||
error
|
error as u64
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -104,8 +82,9 @@ where
|
|||||||
T: ToPrimitive,
|
T: ToPrimitive,
|
||||||
{
|
{
|
||||||
fn from(error: T) -> Self {
|
fn from(error: T) -> Self {
|
||||||
let error = error.to_u32().unwrap_or(0xbad_c0de);
|
let error = error.to_u64().unwrap_or(0xbad_c0de);
|
||||||
match error {
|
match error {
|
||||||
|
CUSTOM_ZERO => InstructionError::CustomError(0),
|
||||||
INVALID_ARGUMENT => InstructionError::InvalidArgument,
|
INVALID_ARGUMENT => InstructionError::InvalidArgument,
|
||||||
INVALID_INSTRUCTION_DATA => InstructionError::InvalidInstructionData,
|
INVALID_INSTRUCTION_DATA => InstructionError::InvalidInstructionData,
|
||||||
INVALID_ACCOUNT_DATA => InstructionError::InvalidAccountData,
|
INVALID_ACCOUNT_DATA => InstructionError::InvalidAccountData,
|
||||||
@ -118,10 +97,11 @@ where
|
|||||||
NOT_ENOUGH_ACCOUNT_KEYS => InstructionError::NotEnoughAccountKeys,
|
NOT_ENOUGH_ACCOUNT_KEYS => InstructionError::NotEnoughAccountKeys,
|
||||||
ACCOUNT_BORROW_FAILED => InstructionError::AccountBorrowFailed,
|
ACCOUNT_BORROW_FAILED => InstructionError::AccountBorrowFailed,
|
||||||
_ => {
|
_ => {
|
||||||
if is_marked_conflicting(error) {
|
// A valid custom error has no bits set in the upper 32
|
||||||
InstructionError::ConflictingError(unmark_conflicting(error))
|
if error >> BUILTIN_BIT_SHIFT == 0 {
|
||||||
|
InstructionError::CustomError(error as u32)
|
||||||
} else {
|
} else {
|
||||||
InstructionError::CustomError(error)
|
InstructionError::InvalidError
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user