diff --git a/Cargo.lock b/Cargo.lock index 6c58e2d6c9..abff33a721 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4440,6 +4440,7 @@ dependencies = [ "hex", "hmac", "itertools 0.9.0", + "lazy_static", "libsecp256k1", "log 0.4.8", "memmap", diff --git a/programs/bpf/Cargo.lock b/programs/bpf/Cargo.lock index 394bfdd967..cf19b778f8 100644 --- a/programs/bpf/Cargo.lock +++ b/programs/bpf/Cargo.lock @@ -2121,6 +2121,7 @@ dependencies = [ "hex", "hmac", "itertools", + "lazy_static", "libsecp256k1", "log", "memmap", diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index 4c01c7ba9f..45b361f6e2 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -42,6 +42,7 @@ generic-array = { version = "0.14.3", default-features = false, features = ["ser hex = "0.4.2" hmac = "0.7.0" itertools = { version = "0.9.0" } +lazy_static = "1.4.0" log = { version = "0.4.8" } memmap = { version = "0.7.0", optional = true } num-derive = { version = "0.3" } diff --git a/sdk/src/decode_error.rs b/sdk/src/decode_error.rs index f50e5bfd00..7ca44864d7 100644 --- a/sdk/src/decode_error.rs +++ b/sdk/src/decode_error.rs @@ -1,6 +1,6 @@ use num_traits::FromPrimitive; -/// Allows customer errors to be decoded back to their original enum +/// Allows custom errors to be decoded back to their original enum pub trait DecodeError { fn decode_custom_error_to_enum(custom: u32) -> Option where diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs index c69001323e..5bf9035232 100644 --- a/sdk/src/lib.rs +++ b/sdk/src/lib.rs @@ -84,7 +84,23 @@ pub mod entrypoint_deprecated; pub mod log; pub mod program; pub mod program_error; + +#[cfg(all(feature = "program", not(target_arch = "bpf")))] +extern crate lazy_static; + +#[cfg(all(feature = "program", not(target_arch = "bpf")))] pub mod program_stubs; + +// Unused `solana_sdk::program_stubs!()` macro retained for source backwards compatibility with v1.3.x programs +#[macro_export] +#[deprecated( + since = "1.4.2", + note = "program_stubs macro is obsolete and can be safely removed" +)] +macro_rules! program_stubs { + () => {}; +} + pub mod serialize_utils; // Modules not usable by on-chain programs diff --git a/sdk/src/log.rs b/sdk/src/log.rs index d96c55e6b1..1c6d5f0ea3 100644 --- a/sdk/src/log.rs +++ b/sdk/src/log.rs @@ -31,10 +31,16 @@ macro_rules! info { /// @param message - Message to print #[inline] pub fn sol_log(message: &str) { + #[cfg(target_arch = "bpf")] unsafe { sol_log_(message.as_ptr(), message.len() as u64); } + + #[cfg(not(target_arch = "bpf"))] + crate::program_stubs::sol_log(message); } + +#[cfg(target_arch = "bpf")] extern "C" { fn sol_log_(message: *const u8, len: u64); } @@ -45,10 +51,16 @@ extern "C" { #[inline] pub fn sol_log_64(arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64) { + #[cfg(target_arch = "bpf")] unsafe { sol_log_64_(arg1, arg2, arg3, arg4, arg5); } + + #[cfg(not(target_arch = "bpf"))] + crate::program_stubs::sol_log_64(arg1, arg2, arg3, arg4, arg5); } + +#[cfg(target_arch = "bpf")] extern "C" { fn sol_log_64_(arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64); } diff --git a/sdk/src/program.rs b/sdk/src/program.rs index 6058175544..c6b4cf3497 100644 --- a/sdk/src/program.rs +++ b/sdk/src/program.rs @@ -1,9 +1,6 @@ #![cfg(feature = "program")] -use crate::{ - account_info::AccountInfo, entrypoint::ProgramResult, entrypoint::SUCCESS, - instruction::Instruction, -}; +use crate::{account_info::AccountInfo, entrypoint::ProgramResult, instruction::Instruction}; /// Invoke a cross-program instruction pub fn invoke(instruction: &Instruction, account_infos: &[AccountInfo]) -> ProgramResult { @@ -32,20 +29,28 @@ pub fn invoke_signed( } } - let result = unsafe { - sol_invoke_signed_rust( - instruction as *const _ as *const u8, - account_infos as *const _ as *const u8, - account_infos.len() as u64, - signers_seeds as *const _ as *const u8, - signers_seeds.len() as u64, - ) - }; - match result { - SUCCESS => Ok(()), - _ => Err(result.into()), + #[cfg(target_arch = "bpf")] + { + let result = unsafe { + sol_invoke_signed_rust( + instruction as *const _ as *const u8, + account_infos as *const _ as *const u8, + account_infos.len() as u64, + signers_seeds as *const _ as *const u8, + signers_seeds.len() as u64, + ) + }; + match result { + crate::entrypoint::SUCCESS => Ok(()), + _ => Err(result.into()), + } } + + #[cfg(not(target_arch = "bpf"))] + crate::program_stubs::sol_invoke_signed(instruction, account_infos, signers_seeds) } + +#[cfg(target_arch = "bpf")] extern "C" { fn sol_invoke_signed_rust( instruction_addr: *const u8, diff --git a/sdk/src/program_stubs.rs b/sdk/src/program_stubs.rs index 9228f5a766..809c70d8de 100644 --- a/sdk/src/program_stubs.rs +++ b/sdk/src/program_stubs.rs @@ -1,36 +1,54 @@ -//! @brief Syscall stubs when building for non-BPF targets +//! @brief Syscall stubs when building for programs for non-BPF targets -#[cfg(not(target_arch = "bpf"))] -#[no_mangle] -/// # Safety -pub unsafe fn sol_log_(message: *const u8, length: u64) { - let slice = std::slice::from_raw_parts(message, length as usize); - let string = std::str::from_utf8(&slice).unwrap(); - println!("{}", string); +use crate::{ + account_info::AccountInfo, entrypoint::ProgramResult, instruction::Instruction, + program_error::ProgramError, +}; +use std::sync::{Arc, RwLock}; + +lazy_static::lazy_static! { + static ref SYSCALL_STUBS: Arc>> = Arc::new(RwLock::new(Box::new(DefaultSyscallStubs {}))); } -#[cfg(not(target_arch = "bpf"))] -#[no_mangle] -pub fn sol_log_64_(arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64) { - println!("{} {} {} {} {}", arg1, arg2, arg3, arg4, arg5); +// The default syscall stubs don't do much, but `set_syscalls()` can be used to swap in +// alternatives +pub fn set_syscall_stubs(syscall_stubs: Box) -> Box { + std::mem::replace(&mut SYSCALL_STUBS.write().unwrap(), syscall_stubs) } -#[cfg(not(target_arch = "bpf"))] -#[no_mangle] -pub fn sol_invoke_signed_rust() { - println!("sol_invoke_signed_rust()"); +pub trait SyscallStubs: Sync + Send { + fn sol_log(&self, message: &str) { + println!("{}", message); + } + fn sol_invoke_signed( + &self, + _instruction: &Instruction, + _account_infos: &[AccountInfo], + _signers_seeds: &[&[&[u8]]], + ) -> ProgramResult { + sol_log("SyscallStubs: sol_invoke_signed() not available"); + Err(ProgramError::InvalidArgument) + } } -#[macro_export] -macro_rules! program_stubs { - () => { - #[cfg(not(target_arch = "bpf"))] - #[test] - fn pull_in_externs() { - use solana_sdk::program_stubs::{sol_invoke_signed_rust, sol_log_, sol_log_64_}; - unsafe { sol_log_("sol_log_".as_ptr(), 8) }; - sol_log_64_(1, 2, 3, 4, 5); - sol_invoke_signed_rust(); - } - }; +struct DefaultSyscallStubs {} +impl SyscallStubs for DefaultSyscallStubs {} + +pub(crate) fn sol_log(message: &str) { + SYSCALL_STUBS.read().unwrap().sol_log(message); +} + +pub(crate) fn sol_log_64(arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64) { + sol_log(&format!("{} {} {} {} {}", arg1, arg2, arg3, arg4, arg5)); +} + +pub(crate) fn sol_invoke_signed( + instruction: &Instruction, + account_infos: &[AccountInfo], + signers_seeds: &[&[&[u8]]], +) -> ProgramResult { + SYSCALL_STUBS + .read() + .unwrap() + .sol_invoke_signed(instruction, account_infos, signers_seeds) } diff --git a/sdk/src/pubkey.rs b/sdk/src/pubkey.rs index d6c1350af7..64e31dd0ac 100644 --- a/sdk/src/pubkey.rs +++ b/sdk/src/pubkey.rs @@ -204,12 +204,17 @@ impl Pubkey { } /// Log a `Pubkey` from a program - #[cfg(feature = "program")] pub fn log(&self) { - extern "C" { - fn sol_log_pubkey(pubkey_addr: *const u8); - }; - unsafe { sol_log_pubkey(self.as_ref() as *const _ as *const u8) }; + #[cfg(all(feature = "program", target_arch = "bpf"))] + { + extern "C" { + fn sol_log_pubkey(pubkey_addr: *const u8); + }; + unsafe { sol_log_pubkey(self.as_ref() as *const _ as *const u8) }; + } + + #[cfg(all(feature = "program", not(target_arch = "bpf")))] + crate::program_stubs::sol_log(&self.to_string()); } }