diff --git a/programs/bpf/benches/bpf_loader.rs b/programs/bpf/benches/bpf_loader.rs index 316f821c7c..355af8603a 100644 --- a/programs/bpf/benches/bpf_loader.rs +++ b/programs/bpf/benches/bpf_loader.rs @@ -80,7 +80,7 @@ fn bench_program_alu(bencher: &mut Bencher) { println!("Interpreted:"); assert_eq!( - 1, /*true*/ + 0, /*success*/ vm.execute_program(&mut inner_iter, &[], &[]).unwrap() ); assert_eq!(ARMSTRONG_LIMIT, LittleEndian::read_u64(&inner_iter)); @@ -106,7 +106,7 @@ fn bench_program_alu(bencher: &mut Bencher) { // vm.jit_compile().unwrap(); // unsafe { // assert_eq!( - // 1, /*true*/ + // 0, /*success*/ // vm.execute_program_jit(&mut inner_iter).unwrap() // ); // } diff --git a/programs/bpf/c/src/bench_alu/bench_alu.c b/programs/bpf/c/src/bench_alu/bench_alu.c index d34a245260..870c67f2eb 100644 --- a/programs/bpf/c/src/bench_alu/bench_alu.c +++ b/programs/bpf/c/src/bench_alu/bench_alu.c @@ -6,7 +6,7 @@ #include -extern bool entrypoint(const uint8_t *input) { +extern uint32_t entrypoint(const uint8_t *input) { uint64_t x = *(uint64_t *) input; uint64_t *result = (uint64_t *) input + 1; uint64_t count = 0; @@ -26,5 +26,5 @@ extern bool entrypoint(const uint8_t *input) { // sol_log_64(x, count, 0, 0, 0); *result = count; - return true; + return SUCCESS; } diff --git a/programs/bpf/c/src/bench_alu/test_bench_alu.c b/programs/bpf/c/src/bench_alu/test_bench_alu.c index 87b4ae8b0c..7219d977e8 100644 --- a/programs/bpf/c/src/bench_alu/test_bench_alu.c +++ b/programs/bpf/c/src/bench_alu/test_bench_alu.c @@ -4,7 +4,7 @@ Test(bench_alu, sanity) { uint64_t input[] = {500, 0}; - cr_assert(entrypoint((uint8_t *) input)); + cr_assert_eq(entrypoint((uint8_t *) input), 0); cr_assert_eq(input[0], 500); cr_assert_eq(input[1], 5); diff --git a/programs/bpf/c/src/bpf_to_bpf/entrypoint.c b/programs/bpf/c/src/bpf_to_bpf/entrypoint.c index 8689ed3be8..3e55cc137b 100644 --- a/programs/bpf/c/src/bpf_to_bpf/entrypoint.c +++ b/programs/bpf/c/src/bpf_to_bpf/entrypoint.c @@ -6,9 +6,9 @@ #include "helper.h" -extern bool entrypoint(const uint8_t *input) { +extern uint32_t entrypoint(const uint8_t *input) { sol_log(__FILE__); helper_function(); sol_log(__FILE__); - return true; + return SUCCESS; } diff --git a/programs/bpf/c/src/move_funds/move_funds.c b/programs/bpf/c/src/move_funds/move_funds.c index 8afae5f8d6..4201f770b3 100644 --- a/programs/bpf/c/src/move_funds/move_funds.c +++ b/programs/bpf/c/src/move_funds/move_funds.c @@ -11,17 +11,27 @@ */ #define NUM_KA 3 -extern bool entrypoint(const uint8_t *input) { +/** + * Custom error for when input serialization fails + */ +#define INVALID_INPUT 1 + +/** + * Custom error for when transaction is not signed properly + */ +#define NOT_SIGNED 2 + +extern uint32_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 false; + return INVALID_INPUT; } if (!params.ka[0].is_signer) { sol_log("Transaction not signed by key 0"); - return false; + return NOT_SIGNED; } int64_t lamports = *(int64_t *)params.data; @@ -32,5 +42,5 @@ extern bool entrypoint(const uint8_t *input) { } else { // sol_log_64(0, 0, 0xFF, *ka[0].lamports, lamports); } - return true; + return SUCCESS; } diff --git a/programs/bpf/c/src/multiple_static/multiple_static.c b/programs/bpf/c/src/multiple_static/multiple_static.c index 04fd0fd2ee..f838f2b3ac 100644 --- a/programs/bpf/c/src/multiple_static/multiple_static.c +++ b/programs/bpf/c/src/multiple_static/multiple_static.c @@ -3,8 +3,8 @@ static const char msg[] = "This is a message"; static const char msg2[] = "This is a different message"; -extern bool entrypoint(const uint8_t *input) { +extern uint32_t entrypoint(const uint8_t *input) { sol_log((char*)msg); sol_log((char*)msg2); - return true; + return SUCCESS; } diff --git a/programs/bpf/c/src/noop++/noop++.cc b/programs/bpf/c/src/noop++/noop++.cc index b5dfa8f135..0f62db39da 100644 --- a/programs/bpf/c/src/noop++/noop++.cc +++ b/programs/bpf/c/src/noop++/noop++.cc @@ -4,19 +4,24 @@ */ #include -extern bool entrypoint(const uint8_t *input) { +/** + * Custom error for when input serialization fails + */ +#define INVALID_INPUT 1 + +extern uint32_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 false; + return INVALID_INPUT; } // Log the provided input parameters. In the case of the no-op // program, no account keys or input data are expected but real // programs will have specific requirements so they can do their work. sol_log_params(¶ms); - return true; + return SUCCESS; } diff --git a/programs/bpf/c/src/noop/noop.c b/programs/bpf/c/src/noop/noop.c index 3a158be883..1f03047f1f 100644 --- a/programs/bpf/c/src/noop/noop.c +++ b/programs/bpf/c/src/noop/noop.c @@ -4,20 +4,24 @@ */ #include -extern bool entrypoint(const uint8_t *input) { +/** + * Custom error for when input serialization fails + */ +#define INVALID_INPUT 1 + +extern uint32_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 false; + return INVALID_INPUT; } // Log the provided input parameters. In the case of the no-op // program, no account keys or input data are expected but real // programs will have specific requirements so they can do their work. sol_log_params(¶ms); - return true; + return SUCCESS; } - diff --git a/programs/bpf/c/src/panic/panic.c b/programs/bpf/c/src/panic/panic.c index c6e773643f..28355737a4 100644 --- a/programs/bpf/c/src/panic/panic.c +++ b/programs/bpf/c/src/panic/panic.c @@ -4,8 +4,7 @@ */ #include -extern bool entrypoint(const uint8_t *input) { +extern uint32_t entrypoint(const uint8_t *input) { sol_panic(); - return true; + return SUCCESS; } - diff --git a/programs/bpf/c/src/relative_call/relative_call.c b/programs/bpf/c/src/relative_call/relative_call.c index 84627ad5ca..7200987b6c 100644 --- a/programs/bpf/c/src/relative_call/relative_call.c +++ b/programs/bpf/c/src/relative_call/relative_call.c @@ -8,9 +8,8 @@ void __attribute__ ((noinline)) helper() { sol_log(__func__); } -extern bool entrypoint(const uint8_t *input) { +extern uint32_t entrypoint(const uint8_t *input) { sol_log(__func__); helper(); - return true; + return SUCCESS; } - diff --git a/programs/bpf/c/src/struct_pass/struct_pass.c b/programs/bpf/c/src/struct_pass/struct_pass.c index f401c1a316..165df0bbaf 100644 --- a/programs/bpf/c/src/struct_pass/struct_pass.c +++ b/programs/bpf/c/src/struct_pass/struct_pass.c @@ -3,15 +3,14 @@ struct foo {const uint8_t *input;}; void foo(const uint8_t *input, struct foo foo) ; -extern bool entrypoint(const uint8_t *input) { +extern uint32_t entrypoint(const uint8_t *input) { struct foo f; f.input = input; foo(input, f); - return true; + return SUCCESS; } void foo(const uint8_t *input, struct foo foo) { sol_log_64(0, 0, 0, (uint64_t)input, (uint64_t)foo.input); sol_assert(input == foo.input); } - diff --git a/programs/bpf/c/src/struct_ret/struct_ret.c b/programs/bpf/c/src/struct_ret/struct_ret.c index 7fd87c398d..62ff212f0e 100644 --- a/programs/bpf/c/src/struct_ret/struct_ret.c +++ b/programs/bpf/c/src/struct_ret/struct_ret.c @@ -1,5 +1,10 @@ #include +/** + * Custom error for when struct doesn't add to 12 + */ +#define INCORRECT_SUM 1 + struct test_struct { uint64_t x; uint64_t y; uint64_t z;}; static struct test_struct __attribute__ ((noinline)) test_function(void) { @@ -10,12 +15,11 @@ static struct test_struct __attribute__ ((noinline)) test_function(void) { return s; } -extern bool entrypoint(const uint8_t* input) { +extern uint32_t entrypoint(const uint8_t* input) { struct test_struct s = test_function(); sol_log("foobar"); if (s.x + s.y + s.z == 12 ) { - return true; + return SUCCESS; } - return false; + return INCORRECT_SUM; } - diff --git a/programs/bpf/rust/128bit/src/lib.rs b/programs/bpf/rust/128bit/src/lib.rs index fe8a06f26f..001ef79dae 100644 --- a/programs/bpf/rust/128bit/src/lib.rs +++ b/programs/bpf/rust/128bit/src/lib.rs @@ -1,10 +1,11 @@ //! @brief Example Rust-based BPF program tests loop iteration extern crate solana_sdk; +use solana_sdk::entrypoint::SUCCESS; use solana_sdk::info; #[no_mangle] -pub extern "C" fn entrypoint(_input: *mut u8) -> bool { +pub extern "C" fn entrypoint(_input: *mut u8) -> u32 { 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); @@ -48,5 +49,5 @@ pub extern "C" fn entrypoint(_input: *mut u8) -> bool { assert_eq!(x, 0x0001_ffff_ffff_ffff_fffe); info!("Success"); - true + SUCCESS } diff --git a/programs/bpf/rust/alloc/src/lib.rs b/programs/bpf/rust/alloc/src/lib.rs index 1281fd1472..bc8de66220 100644 --- a/programs/bpf/rust/alloc/src/lib.rs +++ b/programs/bpf/rust/alloc/src/lib.rs @@ -3,12 +3,13 @@ #[macro_use] extern crate alloc; extern crate solana_sdk; +use solana_sdk::entrypoint::SUCCESS; use solana_sdk::info; use std::alloc::Layout; use std::mem; #[no_mangle] -pub extern "C" fn entrypoint(_input: *mut u8) -> bool { +pub extern "C" fn entrypoint(_input: *mut u8) -> u32 { unsafe { // Confirm large allocation fails @@ -100,5 +101,5 @@ pub extern "C" fn entrypoint(_input: *mut u8) -> bool { } info!("Success"); - true + SUCCESS } diff --git a/programs/bpf/rust/dep_crate/src/lib.rs b/programs/bpf/rust/dep_crate/src/lib.rs index 3a74dccef3..f8f3968163 100644 --- a/programs/bpf/rust/dep_crate/src/lib.rs +++ b/programs/bpf/rust/dep_crate/src/lib.rs @@ -2,10 +2,11 @@ extern crate solana_sdk; use byteorder::{ByteOrder, LittleEndian}; +use solana_sdk::entrypoint::SUCCESS; use solana_sdk::info; #[no_mangle] -pub extern "C" fn entrypoint(_input: *mut u8) -> bool { +pub extern "C" fn entrypoint(_input: *mut u8) -> u32 { let mut buf = [0; 4]; LittleEndian::write_u32(&mut buf, 1_000_000); assert_eq!(1_000_000, LittleEndian::read_u32(&buf)); @@ -15,5 +16,5 @@ pub extern "C" fn entrypoint(_input: *mut u8) -> bool { assert_eq!(-5_000, LittleEndian::read_i16(&buf)); info!("Success"); - true + SUCCESS } diff --git a/programs/bpf/rust/external_spend/src/lib.rs b/programs/bpf/rust/external_spend/src/lib.rs index a50ecca439..1d3cde76d3 100644 --- a/programs/bpf/rust/external_spend/src/lib.rs +++ b/programs/bpf/rust/external_spend/src/lib.rs @@ -6,11 +6,10 @@ use solana_sdk::entrypoint::*; use solana_sdk::pubkey::Pubkey; entrypoint!(process_instruction); -fn process_instruction(_program_id: &Pubkey, ka: &mut [SolKeyedAccount], _data: &[u8]) -> bool { +fn process_instruction(_program_id: &Pubkey, ka: &mut [SolKeyedAccount], _data: &[u8]) -> u32 { // 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 *ka[0].lamports -= 1; - - true + SUCCESS } diff --git a/programs/bpf/rust/iter/src/lib.rs b/programs/bpf/rust/iter/src/lib.rs index 8bd046fb90..7361817cff 100644 --- a/programs/bpf/rust/iter/src/lib.rs +++ b/programs/bpf/rust/iter/src/lib.rs @@ -1,10 +1,11 @@ //! @brief Example Rust-based BPF program tests loop iteration extern crate solana_sdk; +use solana_sdk::entrypoint::SUCCESS; use solana_sdk::info; #[no_mangle] -pub extern "C" fn entrypoint(_input: *mut u8) -> bool { +pub extern "C" fn entrypoint(_input: *mut u8) -> u32 { const ITERS: usize = 100; let ones = [1_u64; ITERS]; let mut sum: u64 = 0; @@ -16,5 +17,5 @@ pub extern "C" fn entrypoint(_input: *mut u8) -> bool { assert_eq!(sum, ITERS as u64); info!("Success"); - true + SUCCESS } diff --git a/programs/bpf/rust/many_args/src/lib.rs b/programs/bpf/rust/many_args/src/lib.rs index 6cee830644..fef3441625 100644 --- a/programs/bpf/rust/many_args/src/lib.rs +++ b/programs/bpf/rust/many_args/src/lib.rs @@ -2,10 +2,11 @@ mod helper; extern crate solana_sdk; +use solana_sdk::entrypoint::SUCCESS; use solana_sdk::info; #[no_mangle] -pub extern "C" fn entrypoint(_input: *mut u8) -> bool { +pub extern "C" fn entrypoint(_input: *mut u8) -> u32 { info!("Call same package"); assert_eq!(crate::helper::many_args(1, 2, 3, 4, 5, 6, 7, 8, 9), 45); @@ -24,5 +25,5 @@ pub extern "C" fn entrypoint(_input: *mut u8) -> bool { ); info!("Success"); - true + SUCCESS } diff --git a/programs/bpf/rust/noop/src/lib.rs b/programs/bpf/rust/noop/src/lib.rs index 1b31b415b7..a9e1918d5a 100644 --- a/programs/bpf/rust/noop/src/lib.rs +++ b/programs/bpf/rust/noop/src/lib.rs @@ -21,7 +21,7 @@ fn return_sstruct() -> SStruct { } entrypoint!(process_instruction); -fn process_instruction(program_id: &Pubkey, ka: &mut [SolKeyedAccount], data: &[u8]) -> bool { +fn process_instruction(program_id: &Pubkey, ka: &mut [SolKeyedAccount], data: &[u8]) -> u32 { info!("Program identifier:"); program_id.log(); @@ -56,7 +56,7 @@ fn process_instruction(program_id: &Pubkey, ka: &mut [SolKeyedAccount], data: &[ } info!("Success"); - true + SUCCESS } #[cfg(test)] diff --git a/programs/bpf/rust/panic/src/lib.rs b/programs/bpf/rust/panic/src/lib.rs index c862901568..4b5463591b 100644 --- a/programs/bpf/rust/panic/src/lib.rs +++ b/programs/bpf/rust/panic/src/lib.rs @@ -3,6 +3,6 @@ extern crate solana_sdk; #[no_mangle] -pub extern "C" fn entrypoint(_input: *mut u8) -> bool { +pub extern "C" fn entrypoint(_input: *mut u8) -> u32 { panic!(); } diff --git a/programs/bpf/rust/param_passing/src/lib.rs b/programs/bpf/rust/param_passing/src/lib.rs index d2e737662f..1170fac991 100644 --- a/programs/bpf/rust/param_passing/src/lib.rs +++ b/programs/bpf/rust/param_passing/src/lib.rs @@ -2,10 +2,11 @@ extern crate solana_sdk; use solana_bpf_rust_param_passing_dep::{Data, TestDep}; +use solana_sdk::entrypoint::SUCCESS; use solana_sdk::info; #[no_mangle] -pub extern "C" fn entrypoint(_input: *mut u8) -> bool { +pub extern "C" fn entrypoint(_input: *mut u8) -> u32 { let array = [0xA, 0xB, 0xC, 0xD, 0xE, 0xF]; let data = Data { twentyone: 21u64, @@ -18,5 +19,6 @@ pub extern "C" fn entrypoint(_input: *mut u8) -> bool { let test_dep = TestDep::new(&data, 1, 2, 3, 4, 5); info!(0, 0, 0, 0, test_dep.thirty); - test_dep.thirty == 30 + assert!(test_dep.thirty == 30); + SUCCESS } diff --git a/programs/bpf/rust/tick_height/src/lib.rs b/programs/bpf/rust/tick_height/src/lib.rs index 888f67f7ea..39a759a467 100644 --- a/programs/bpf/rust/tick_height/src/lib.rs +++ b/programs/bpf/rust/tick_height/src/lib.rs @@ -7,10 +7,10 @@ use solana_sdk::pubkey::Pubkey; use solana_sdk::{entrypoint, info}; entrypoint!(process_instruction); -fn process_instruction(_program_id: &Pubkey, ka: &mut [SolKeyedAccount], _data: &[u8]) -> bool { +fn process_instruction(_program_id: &Pubkey, ka: &mut [SolKeyedAccount], _data: &[u8]) -> u32 { let tick_height = LittleEndian::read_u64(ka[2].data); assert_eq!(10u64, tick_height); info!("Success"); - true + SUCCESS } diff --git a/programs/bpf_loader_api/src/lib.rs b/programs/bpf_loader_api/src/lib.rs index 7882b557f4..d119123324 100644 --- a/programs/bpf_loader_api/src/lib.rs +++ b/programs/bpf_loader_api/src/lib.rs @@ -20,6 +20,7 @@ use solana_sdk::account::KeyedAccount; use solana_sdk::instruction::InstructionError; use solana_sdk::loader_instruction::LoaderInstruction; use solana_sdk::pubkey::Pubkey; +use std::convert::TryFrom; use std::io::prelude::*; use std::io::Error; use std::mem; @@ -136,12 +137,18 @@ pub fn process_instruction( let mut v = serialize_parameters(program_id, params, &data); match vm.execute_program(v.as_mut_slice(), &[], &[heap_region]) { - Ok(status) => { - if 0 == status { - warn!("BPF program failed: {}", status); + Ok(status) => match u32::try_from(status) { + Ok(status) => { + if status > 0 { + warn!("BPF program failed: {}", status); + return Err(InstructionError::CustomError(status)); + } + } + Err(e) => { + warn!("BPF VM encountered invalid status: {}", e); return Err(InstructionError::GenericError); } - } + }, Err(e) => { warn!("BPF VM failed to run program: {}", e); return Err(InstructionError::GenericError); diff --git a/sdk/bpf/c/inc/solana_sdk.h b/sdk/bpf/c/inc/solana_sdk.h index d61e8a344d..fadf26aba5 100644 --- a/sdk/bpf/c/inc/solana_sdk.h +++ b/sdk/bpf/c/inc/solana_sdk.h @@ -51,6 +51,11 @@ static_assert(sizeof(uint64_t) == 8); */ #define NULL 0 +/** + * SUCCESS return value + */ +#define SUCCESS 0 + /** * Boolean type */ @@ -319,9 +324,9 @@ SOL_FN_PREFIX void sol_log_params(const SolParameters *params) { * Program instruction entrypoint * * @param input Buffer of serialized input parameters. Use sol_deserialize() to decode - * @return true if the instruction executed successfully + * @return 0 if the instruction executed successfully */ -bool entrypoint(const uint8_t *input); +uint32_t entrypoint(const uint8_t *input); #ifdef SOL_TEST diff --git a/sdk/src/entrypoint.rs b/sdk/src/entrypoint.rs index 2deb049272..2bc6917b55 100644 --- a/sdk/src/entrypoint.rs +++ b/sdk/src/entrypoint.rs @@ -28,6 +28,9 @@ pub struct SolKeyedAccount<'a> { pub type ProcessInstruction = fn(program_id: &Pubkey, accounts: &mut [SolKeyedAccount], data: &[u8]) -> bool; +/// Programs indicate success with a return value of 0 +pub const SUCCESS: u32 = 0; + /// Declare entrypoint of the program. /// /// Deserialize the program input parameters and call @@ -38,13 +41,10 @@ pub type ProcessInstruction = macro_rules! entrypoint { ($process_instruction:ident) => { #[no_mangle] - pub unsafe extern "C" fn entrypoint(input: *mut u8) -> bool { + pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u32 { unsafe { - if let Ok((program_id, mut kas, data)) = $crate::entrypoint::deserialize(input) { - $process_instruction(&program_id, &mut kas, &data) - } else { - false - } + let (program_id, mut kas, data) = $crate::entrypoint::deserialize(input); + $process_instruction(&program_id, &mut kas, &data) } } }; @@ -54,7 +54,7 @@ macro_rules! entrypoint { #[allow(clippy::type_complexity)] pub unsafe fn deserialize<'a>( input: *mut u8, -) -> Result<(&'a Pubkey, Vec>, &'a [u8]), ()> { +) -> (&'a Pubkey, Vec>, &'a [u8]) { let mut offset: usize = 0; // Number of KeyedAccounts present @@ -113,5 +113,5 @@ pub unsafe fn deserialize<'a>( let program_id: &Pubkey = &*(input.add(offset) as *const Pubkey); - Ok((program_id, kas, data)) + (program_id, kas, data) } diff --git a/sdk/src/instruction.rs b/sdk/src/instruction.rs index 7e7f9fa79b..026c89d606 100644 --- a/sdk/src/instruction.rs +++ b/sdk/src/instruction.rs @@ -70,6 +70,9 @@ pub enum InstructionError { /// CustomError allows on-chain programs to implement program-specific error types and see /// them returned by the Solana runtime. A CustomError may be any type that is represented /// as 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), }