diff --git a/programs/bpf/rust/error_handling/src/lib.rs b/programs/bpf/rust/error_handling/src/lib.rs index 31ea3e4af6..7b7cdf481a 100644 --- a/programs/bpf/rust/error_handling/src/lib.rs +++ b/programs/bpf/rust/error_handling/src/lib.rs @@ -28,7 +28,7 @@ impl From for ProgramError { entrypoint!(process_instruction); fn process_instruction( _program_id: &Pubkey, - _accounts: &[AccountInfo], + accounts: &[AccountInfo], instruction_data: &[u8], ) -> Result<(), ProgramError> { match instruction_data[0] { @@ -38,7 +38,7 @@ fn process_instruction( } 2 => { info!("return a builtin"); - Err(ProgramError::AccountBorrowFailed) + Err(ProgramError::InvalidAccountData) } 3 => { info!("return custom error"); @@ -52,6 +52,12 @@ fn process_instruction( info!("return error that conflicts with builtin"); Err(MyError::ConflictingBuiltin.into()) } + 6 => { + let data = accounts[0].try_borrow_mut_data()?; + let data2 = accounts[0].try_borrow_mut_data()?; + assert_eq!(*data, *data2); + Ok(()) + } _ => { info!("Unrecognized command"); Err(ProgramError::InvalidInstructionData) diff --git a/programs/bpf/tests/programs.rs b/programs/bpf/tests/programs.rs index 775b606eae..3cb6623f49 100644 --- a/programs/bpf/tests/programs.rs +++ b/programs/bpf/tests/programs.rs @@ -384,7 +384,7 @@ mod bpf { let result = bank_client.send_instruction(&mint_keypair, instruction); assert_eq!( result.unwrap_err().unwrap(), - TransactionError::InstructionError(0, InstructionError::AccountBorrowFailed) + TransactionError::InstructionError(0, InstructionError::InvalidAccountData) ); let instruction = Instruction::new(program_id, &3u8, account_metas.clone()); @@ -413,6 +413,13 @@ mod bpf { let instruction = Instruction::new(program_id, &6u8, account_metas.clone()); let result = bank_client.send_instruction(&mint_keypair, instruction); + assert_eq!( + result.unwrap_err().unwrap(), + TransactionError::InstructionError(0, InstructionError::AccountBorrowFailed) + ); + + let instruction = Instruction::new(program_id, &7u8, account_metas.clone()); + let result = bank_client.send_instruction(&mint_keypair, instruction); assert_eq!( result.unwrap_err().unwrap(), TransactionError::InstructionError(0, InstructionError::InvalidInstructionData) diff --git a/sdk/src/account_info.rs b/sdk/src/account_info.rs index 7d046cc783..62e8748199 100644 --- a/sdk/src/account_info.rs +++ b/sdk/src/account_info.rs @@ -1,5 +1,9 @@ -use crate::{account::Account, pubkey::Pubkey}; -use std::{cell::RefCell, cmp, fmt, rc::Rc}; +use crate::{account::Account, program_error::ProgramError, pubkey::Pubkey}; +use std::{ + cell::{Ref, RefCell, RefMut}, + cmp, fmt, + rc::Rc, +}; /// Account information #[derive(Clone)] @@ -55,14 +59,50 @@ impl<'a> AccountInfo<'a> { **self.lamports.borrow() } + pub fn try_lamports(&self) -> Result { + Ok(**self.try_borrow_lamports()?) + } + pub fn data_len(&self) -> usize { self.data.borrow().len() } + pub fn try_data_len(&self) -> Result { + Ok(self.try_borrow_data()?.len()) + } + pub fn data_is_empty(&self) -> bool { self.data.borrow().is_empty() } + pub fn try_data_is_empty(&self) -> Result { + Ok(self.try_borrow_data()?.is_empty()) + } + + pub fn try_borrow_lamports(&self) -> Result, ProgramError> { + self.lamports + .try_borrow() + .map_err(|_| ProgramError::AccountBorrowFailed) + } + + pub fn try_borrow_mut_lamports(&self) -> Result, ProgramError> { + self.lamports + .try_borrow_mut() + .map_err(|_| ProgramError::AccountBorrowFailed) + } + + pub fn try_borrow_data(&self) -> Result, ProgramError> { + self.data + .try_borrow() + .map_err(|_| ProgramError::AccountBorrowFailed) + } + + pub fn try_borrow_mut_data(&self) -> Result, ProgramError> { + self.data + .try_borrow_mut() + .map_err(|_| ProgramError::AccountBorrowFailed) + } + pub fn new( key: &'a Pubkey, is_signer: bool, diff --git a/sdk/src/instruction.rs b/sdk/src/instruction.rs index 2b7311016b..efee534cd1 100644 --- a/sdk/src/instruction.rs +++ b/sdk/src/instruction.rs @@ -75,10 +75,10 @@ pub enum InstructionError { /// The instruction expected an executable account AccountNotExecutable, - /// Failed to borrow a reference to an account, already borrowed + /// Failed to borrow a reference to account data, already borrowed AccountBorrowFailed, - /// Account has an outstanding reference after a program's execution + /// Account data has an outstanding reference after a program's execution AccountBorrowOutstanding, /// The same account was multiply passed to an on-chain program's entrypoint, but the program diff --git a/sdk/src/program_error.rs b/sdk/src/program_error.rs index 122aeaeb0f..e9563d9d36 100644 --- a/sdk/src/program_error.rs +++ b/sdk/src/program_error.rs @@ -30,7 +30,7 @@ pub enum ProgramError { UninitializedAccount, /// The instruction expected additional account keys NotEnoughAccountKeys, - /// Failed to borrow a reference to an account, already borrowed + /// Failed to borrow a reference to account data, already borrowed AccountBorrowFailed, }