diff --git a/programs/bpf/rust/external_spend/src/lib.rs b/programs/bpf/rust/external_spend/src/lib.rs index 1d3cde76d3..b7861a9889 100644 --- a/programs/bpf/rust/external_spend/src/lib.rs +++ b/programs/bpf/rust/external_spend/src/lib.rs @@ -1,15 +1,14 @@ //! @brief Example Rust-based BPF program that moves a lamport from one account to another extern crate solana_sdk; -use solana_sdk::entrypoint; -use solana_sdk::entrypoint::*; -use solana_sdk::pubkey::Pubkey; +use solana_sdk::{account_info::AccountInfo, entrypoint, entrypoint::SUCCESS, pubkey::Pubkey}; entrypoint!(process_instruction); -fn process_instruction(_program_id: &Pubkey, ka: &mut [SolKeyedAccount], _data: &[u8]) -> u32 { +fn process_instruction(_program_id: &Pubkey, accounts: &mut [AccountInfo], _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; + *accounts[0].lamports -= 1; + SUCCESS } diff --git a/programs/bpf/rust/noop/src/lib.rs b/programs/bpf/rust/noop/src/lib.rs index a9e1918d5a..d89586b615 100644 --- a/programs/bpf/rust/noop/src/lib.rs +++ b/programs/bpf/rust/noop/src/lib.rs @@ -3,10 +3,9 @@ #![allow(unreachable_code)] extern crate solana_sdk; -use solana_sdk::entrypoint::*; -use solana_sdk::log::*; -use solana_sdk::pubkey::Pubkey; -use solana_sdk::{entrypoint, info}; +use solana_sdk::{ + account_info::AccountInfo, entrypoint, entrypoint::SUCCESS, info, log::*, pubkey::Pubkey, +}; #[derive(Debug, PartialEq)] struct SStruct { @@ -21,7 +20,7 @@ fn return_sstruct() -> SStruct { } entrypoint!(process_instruction); -fn process_instruction(program_id: &Pubkey, ka: &mut [SolKeyedAccount], data: &[u8]) -> u32 { +fn process_instruction(program_id: &Pubkey, accounts: &mut [AccountInfo], data: &[u8]) -> u32 { info!("Program identifier:"); program_id.log(); @@ -29,7 +28,7 @@ fn process_instruction(program_id: &Pubkey, ka: &mut [SolKeyedAccount], data: &[ // 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. info!("Account keys and instruction input data:"); - sol_log_params(ka, data); + sol_log_params(accounts, data); { // Test - use std methods, unwrap diff --git a/programs/bpf/rust/tick_height/src/lib.rs b/programs/bpf/rust/tick_height/src/lib.rs index 39a759a467..c7f1cbc386 100644 --- a/programs/bpf/rust/tick_height/src/lib.rs +++ b/programs/bpf/rust/tick_height/src/lib.rs @@ -2,13 +2,13 @@ extern crate solana_sdk; use byteorder::{ByteOrder, LittleEndian}; -use solana_sdk::entrypoint::*; -use solana_sdk::pubkey::Pubkey; -use solana_sdk::{entrypoint, info}; +use solana_sdk::{ + account_info::AccountInfo, entrypoint, entrypoint::SUCCESS, info, pubkey::Pubkey, +}; entrypoint!(process_instruction); -fn process_instruction(_program_id: &Pubkey, ka: &mut [SolKeyedAccount], _data: &[u8]) -> u32 { - let tick_height = LittleEndian::read_u64(ka[2].data); +fn process_instruction(_program_id: &Pubkey, accounts: &mut [AccountInfo], _data: &[u8]) -> u32 { + let tick_height = LittleEndian::read_u64(accounts[2].data); assert_eq!(10u64, tick_height); info!("Success"); diff --git a/sdk/src/account_info.rs b/sdk/src/account_info.rs new file mode 100644 index 0000000000..a557ff629e --- /dev/null +++ b/sdk/src/account_info.rs @@ -0,0 +1,48 @@ +use crate::pubkey::Pubkey; +use std::{cmp, fmt}; + +/// AccountInfo +pub struct AccountInfo<'a> { + /// Public key of the account + pub key: &'a Pubkey, + /// Public key of the account + pub is_signer: bool, + /// Number of lamports owned by this account + pub lamports: &'a mut u64, + /// On-chain data within this account + pub data: &'a mut [u8], + /// Program that owns this account + pub owner: &'a Pubkey, +} + +impl<'a> fmt::Debug for AccountInfo<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let data_len = cmp::min(64, self.data.len()); + let data_str = if data_len > 0 { + format!(" data: {}", hex::encode(self.data[..data_len].to_vec())) + } else { + "".to_string() + }; + write!( + f, + "AccountInfo {{ lamports: {} data.len: {} owner: {} {} }}", + self.lamports, + self.data.len(), + self.owner, + data_str, + ) + } +} + +impl<'a> AccountInfo<'a> { + pub fn deserialize_data(&self) -> Result { + bincode::deserialize(&self.data) + } + + pub fn serialize_data(&mut self, state: &T) -> Result<(), bincode::Error> { + if bincode::serialized_size(state)? > self.data.len() as u64 { + return Err(Box::new(bincode::ErrorKind::SizeLimit)); + } + bincode::serialize_into(&mut self.data[..], state) + } +} diff --git a/sdk/src/entrypoint.rs b/sdk/src/entrypoint.rs index 2bc6917b55..e12da85e3f 100644 --- a/sdk/src/entrypoint.rs +++ b/sdk/src/entrypoint.rs @@ -1,32 +1,19 @@ //! @brief Solana Rust-based BPF program entrypoint and its parameter types + extern crate alloc; -use crate::pubkey::Pubkey; +use crate::{account_info::AccountInfo, pubkey::Pubkey}; use alloc::vec::Vec; use core::mem::size_of; use core::slice::{from_raw_parts, from_raw_parts_mut}; -/// Keyed Account -pub struct SolKeyedAccount<'a> { - /// Public key of the account - pub key: &'a Pubkey, - /// Public key of the account - pub is_signer: bool, - /// Number of lamports owned by this account - pub lamports: &'a mut u64, - /// On-chain data within this account - pub data: &'a mut [u8], - /// Program that owns this account - pub owner: &'a Pubkey, -} - /// User implemented program entrypoint /// /// program_id: Program ID of the currently executing program /// accounts: Accounts passed as part of the instruction /// data: Instruction data pub type ProcessInstruction = - fn(program_id: &Pubkey, accounts: &mut [SolKeyedAccount], data: &[u8]) -> bool; + fn(program_id: &Pubkey, accounts: &mut [AccountInfo], data: &[u8]) -> bool; /// Programs indicate success with a return value of 0 pub const SUCCESS: u32 = 0; @@ -42,9 +29,9 @@ macro_rules! entrypoint { ($process_instruction:ident) => { #[no_mangle] pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u32 { - unsafe { - let (program_id, mut kas, data) = $crate::entrypoint::deserialize(input); - $process_instruction(&program_id, &mut kas, &data) + unsafe { + let (program_id, mut accounts, data) = $crate::entrypoint::deserialize(input); + $process_instruction(&program_id, &mut accounts, &data) } } }; @@ -52,21 +39,19 @@ macro_rules! entrypoint { /// Deserialize the input parameters #[allow(clippy::type_complexity)] -pub unsafe fn deserialize<'a>( - input: *mut u8, -) -> (&'a Pubkey, Vec>, &'a [u8]) { +pub unsafe fn deserialize<'a>(input: *mut u8) -> (&'a Pubkey, Vec>, &'a [u8]) { let mut offset: usize = 0; - // Number of KeyedAccounts present + // Number of accounts present #[allow(clippy::cast_ptr_alignment)] - let num_ka = *(input.add(offset) as *const u64) as usize; + let num_accounts = *(input.add(offset) as *const u64) as usize; offset += size_of::(); - // KeyedAccounts + // Account Infos - let mut kas = Vec::with_capacity(num_ka); - for _ in 0..num_ka { + let mut accounts = Vec::with_capacity(num_accounts); + for _ in 0..num_accounts { let is_signer = { #[allow(clippy::cast_ptr_alignment)] let is_signer_val = *(input.add(offset) as *const u64); @@ -91,7 +76,7 @@ pub unsafe fn deserialize<'a>( let owner: &Pubkey = &*(input.add(offset) as *const Pubkey); offset += size_of::(); - kas.push(SolKeyedAccount { + accounts.push(AccountInfo { key, is_signer, lamports, @@ -113,5 +98,5 @@ pub unsafe fn deserialize<'a>( let program_id: &Pubkey = &*(input.add(offset) as *const Pubkey); - (program_id, kas, data) + (program_id, accounts, data) } diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs index edcaf41d82..c0dc26e6d9 100644 --- a/sdk/src/lib.rs +++ b/sdk/src/lib.rs @@ -3,6 +3,8 @@ pub mod pubkey; // On-chain program modules #[cfg(feature = "program")] +pub mod account_info; +#[cfg(feature = "program")] pub mod entrypoint; #[cfg(feature = "program")] pub mod log; diff --git a/sdk/src/log.rs b/sdk/src/log.rs index 6e7cc44bdf..27f31c6be0 100644 --- a/sdk/src/log.rs +++ b/sdk/src/log.rs @@ -1,6 +1,6 @@ //! @brief Solana Rust-based BPF program logging -use crate::entrypoint::SolKeyedAccount; +use crate::account_info::AccountInfo; /// Prints a string /// There are two forms and are fast @@ -63,20 +63,20 @@ pub fn sol_log_slice(slice: &[u8]) { /// @param ka - A pointer to an array of `SolKeyedAccounts` to print /// @param data - A pointer to the instruction data to print #[allow(dead_code)] -pub fn sol_log_params(ka: &[SolKeyedAccount], data: &[u8]) { - for (i, k) in ka.iter().enumerate() { +pub fn sol_log_params(accounts: &[AccountInfo], data: &[u8]) { + for (i, account) in accounts.iter().enumerate() { sol_log("SolKeyedAccount"); sol_log_64(0, 0, 0, 0, i as u64); sol_log("- Is signer"); - sol_log_64(0, 0, 0, 0, k.is_signer as u64); + sol_log_64(0, 0, 0, 0, account.is_signer as u64); sol_log("- Key"); - k.key.log(); + account.key.log(); sol_log("- Lamports"); - sol_log_64(0, 0, 0, 0, *k.lamports); + sol_log_64(0, 0, 0, 0, *account.lamports); sol_log("- AccountData"); - sol_log_slice(k.data); + sol_log_slice(account.data); sol_log("- Owner"); - k.owner.log(); + account.owner.log(); } sol_log("Instruction data"); sol_log_slice(data);