Skip to content
This repository was archived by the owner on Feb 13, 2025. It is now read-only.

Commit 202b242

Browse files
committed
Use scope borrows
1 parent 165a5f3 commit 202b242

21 files changed

+414
-279
lines changed

program/src/entrypoint.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ pub fn process_instruction(
4343
#[cfg(feature = "logging")]
4444
pinocchio::msg!("Instruction: InitializeMint");
4545

46-
process_initialize_mint(accounts, instruction_data, true)
46+
process_initialize_mint(accounts, instruction_data)
4747
}
4848

4949
// 3 - Transfer

program/src/processor/amount_to_ui_amount.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ pub fn process_amount_to_ui_amount(
2323

2424
let mint_info = accounts.first().ok_or(ProgramError::NotEnoughAccountKeys)?;
2525
check_account_owner(mint_info)?;
26-
// SAFETY: there is a single borrow to the `Mint` account.
26+
// SAFETY: there is a single immutable borrow of the `Mint` account data.
2727
let mint = unsafe {
2828
load::<Mint>(mint_info.borrow_data_unchecked()).map_err(|_| TokenError::InvalidMint)?
2929
};

program/src/processor/close_account.rs

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use pinocchio::{
33
};
44
use token_interface::{
55
error::TokenError,
6-
state::{account::Account, load_mut},
6+
state::{account::Account, load},
77
};
88

99
use super::validate_owner;
@@ -26,24 +26,30 @@ pub fn process_close_account(accounts: &[AccountInfo]) -> ProgramResult {
2626
return Err(ProgramError::InvalidAccountData);
2727
}
2828

29-
let source_account =
30-
unsafe { load_mut::<Account>(source_account_info.borrow_mut_data_unchecked())? };
31-
32-
if !source_account.is_native() && source_account.amount() != 0 {
33-
return Err(TokenError::NonNativeHasBalance.into());
34-
}
35-
36-
let authority = source_account
37-
.close_authority()
38-
.unwrap_or(&source_account.owner);
39-
40-
if !source_account.is_owned_by_system_program_or_incinerator() {
41-
validate_owner(authority, authority_info, remaining)?;
42-
} else if destination_account_info.key() != &INCINERATOR_ID {
43-
return Err(ProgramError::InvalidAccountData);
29+
// SAFETY: scoped immutable borrow of the source account data. Changes to the
30+
// account info data happens after the borrow ends.
31+
{
32+
let source_account =
33+
unsafe { load::<Account>(source_account_info.borrow_data_unchecked())? };
34+
35+
if !source_account.is_native() && source_account.amount() != 0 {
36+
return Err(TokenError::NonNativeHasBalance.into());
37+
}
38+
39+
let authority = source_account
40+
.close_authority()
41+
.unwrap_or(&source_account.owner);
42+
43+
if !source_account.is_owned_by_system_program_or_incinerator() {
44+
validate_owner(authority, authority_info, remaining)?;
45+
} else if destination_account_info.key() != &INCINERATOR_ID {
46+
return Err(ProgramError::InvalidAccountData);
47+
}
4448
}
4549

4650
let destination_starting_lamports = destination_account_info.lamports();
51+
// SAFETY: there are no other borrows of the source and destination accounts
52+
// at this point.
4753
unsafe {
4854
// Moves the lamports to the destination account.
4955
*destination_account_info.borrow_mut_lamports_unchecked() = destination_starting_lamports

program/src/processor/get_account_data_size.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ pub fn process_get_account_data_size(accounts: &[AccountInfo]) -> ProgramResult
1616

1717
// Make sure the mint is valid.
1818
check_account_owner(mint_info)?;
19-
19+
// SAFETY: there is a single immutable borrow of the `Mint` account data.
20+
//
21+
// The `Mint` account is loaded to check if it is initialized.
2022
let _ = unsafe {
2123
load::<Mint>(mint_info.borrow_data_unchecked()).map_err(|_| TokenError::InvalidMint)?
2224
};

program/src/processor/initialize_immutable_owner.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use token_interface::{
77
#[inline(always)]
88
pub fn process_initialize_immutable_owner(accounts: &[AccountInfo]) -> ProgramResult {
99
let token_account_info = accounts.first().ok_or(ProgramError::NotEnoughAccountKeys)?;
10-
10+
// SAFETY: there is a single immutable borrow of the `Account` account data.
1111
let account = unsafe { load_unchecked::<Account>(token_account_info.borrow_data_unchecked())? };
1212

1313
if account.is_initialized() {
Lines changed: 5 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -1,114 +1,8 @@
1-
use core::{marker::PhantomData, mem::size_of};
2-
use pinocchio::{
3-
account_info::AccountInfo,
4-
program_error::ProgramError,
5-
pubkey::Pubkey,
6-
sysvars::{rent::Rent, Sysvar},
7-
ProgramResult,
8-
};
9-
use token_interface::{
10-
error::TokenError,
11-
state::{load_mut_unchecked, mint::Mint, Initializable},
12-
};
1+
use pinocchio::{account_info::AccountInfo, ProgramResult};
132

14-
#[inline(always)]
15-
pub fn process_initialize_mint(
16-
accounts: &[AccountInfo],
17-
instruction_data: &[u8],
18-
rent_sysvar_account: bool,
19-
) -> ProgramResult {
20-
// Validates the instruction data.
21-
22-
let args = InitializeMint::try_from_bytes(instruction_data)?;
23-
24-
// Validates the accounts.
25-
26-
let (mint_info, rent_sysvar_info) = if rent_sysvar_account {
27-
let [mint_info, rent_sysvar_info, _remaining @ ..] = accounts else {
28-
return Err(ProgramError::NotEnoughAccountKeys);
29-
};
30-
(mint_info, Some(rent_sysvar_info))
31-
} else {
32-
let [mint_info, _remaining @ ..] = accounts else {
33-
return Err(ProgramError::NotEnoughAccountKeys);
34-
};
35-
(mint_info, None)
36-
};
37-
38-
let mint = unsafe { load_mut_unchecked::<Mint>(mint_info.borrow_mut_data_unchecked())? };
39-
40-
if mint.is_initialized() {
41-
return Err(TokenError::AlreadyInUse.into());
42-
}
43-
44-
// Check rent-exempt status of the mint account.
45-
46-
let is_exempt = if let Some(rent_sysvar_info) = rent_sysvar_info {
47-
let rent = unsafe { Rent::from_bytes(rent_sysvar_info.borrow_data_unchecked())? };
48-
rent.is_exempt(mint_info.lamports(), size_of::<Mint>())
49-
} else {
50-
Rent::get()?.is_exempt(mint_info.lamports(), size_of::<Mint>())
51-
};
52-
53-
if !is_exempt {
54-
return Err(TokenError::NotRentExempt.into());
55-
}
56-
57-
// Initialize the mint.
58-
59-
mint.set_initialized(true);
60-
mint.set_mint_authority(args.mint_authority());
61-
mint.decimals = args.decimals();
3+
use super::shared;
624

63-
if let Some(freeze_authority) = args.freeze_authority() {
64-
mint.set_freeze_authority(freeze_authority);
65-
}
66-
67-
Ok(())
68-
}
69-
70-
/// Instruction data for the `InitializeMint` instruction.
71-
pub struct InitializeMint<'a> {
72-
raw: *const u8,
73-
74-
_data: PhantomData<&'a [u8]>,
75-
}
76-
77-
impl InitializeMint<'_> {
78-
#[inline]
79-
pub fn try_from_bytes(bytes: &[u8]) -> Result<InitializeMint, ProgramError> {
80-
// The minimum expected size of the instruction data.
81-
// - decimals (1 byte)
82-
// - mint_authority (32 bytes)
83-
// - option + freeze_authority (1 byte + 32 bytes)
84-
if bytes.len() < 34 {
85-
return Err(ProgramError::InvalidInstructionData);
86-
}
87-
88-
Ok(InitializeMint {
89-
raw: bytes.as_ptr(),
90-
_data: PhantomData,
91-
})
92-
}
93-
94-
#[inline]
95-
pub fn decimals(&self) -> u8 {
96-
unsafe { *self.raw }
97-
}
98-
99-
#[inline]
100-
pub fn mint_authority(&self) -> &Pubkey {
101-
unsafe { &*(self.raw.add(1) as *const Pubkey) }
102-
}
103-
104-
#[inline]
105-
pub fn freeze_authority(&self) -> Option<&Pubkey> {
106-
unsafe {
107-
if *self.raw.add(33) == 0 {
108-
Option::None
109-
} else {
110-
Option::Some(&*(self.raw.add(34) as *const Pubkey))
111-
}
112-
}
113-
}
5+
#[inline(always)]
6+
pub fn process_initialize_mint(accounts: &[AccountInfo], instruction_data: &[u8]) -> ProgramResult {
7+
shared::initialize_mint::process_initialize_mint(accounts, instruction_data, true)
1148
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
use pinocchio::{account_info::AccountInfo, ProgramResult};
22

3-
use super::initialize_mint::process_initialize_mint;
3+
use super::shared;
44

55
#[inline(always)]
66
pub fn process_initialize_mint2(
77
accounts: &[AccountInfo],
88
instruction_data: &[u8],
99
) -> ProgramResult {
10-
process_initialize_mint(accounts, instruction_data, false)
10+
shared::initialize_mint::process_initialize_mint(accounts, instruction_data, false)
1111
}

program/src/processor/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,9 @@ fn validate_owner(
106106
if owner_account_info.data_len() == Multisig::LEN
107107
&& owner_account_info.owner() == &TOKEN_PROGRAM_ID
108108
{
109+
// SAFETY: the caller guarantees that there are no mutable borrows of `owner_account_info`
110+
// account data, so it is ok to have multiple immutable borrows (this would normally only
111+
// happen if the account/mint is the same as the owner).
109112
let multisig = unsafe { load::<Multisig>(owner_account_info.borrow_data_unchecked())? };
110113

111114
let mut num_signers = 0;

program/src/processor/revoke.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,15 @@ pub fn process_revoke(accounts: &[AccountInfo], _instruction_data: &[u8]) -> Pro
1111
let [source_account_info, owner_info, remaning @ ..] = accounts else {
1212
return Err(ProgramError::NotEnoughAccountKeys);
1313
};
14-
14+
// SAFETY: there are no other borrows of the `source_account` data.
1515
let source_account =
1616
unsafe { load_mut::<Account>(source_account_info.borrow_mut_data_unchecked())? };
1717

1818
if source_account.is_frozen() {
1919
return Err(TokenError::AccountFrozen.into());
2020
}
2121

22+
// TODO: can source == owner?
2223
validate_owner(&source_account.owner, owner_info, remaning)?;
2324

2425
source_account.clear_delegate();

program/src/processor/set_authority.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ pub fn process_set_authority(accounts: &[AccountInfo], instruction_data: &[u8])
2727
};
2828

2929
if account_info.data_len() == Account::LEN {
30+
// SAFETY: there are no other active borrows of the `account` data.
3031
let account = unsafe { load_mut::<Account>(account_info.borrow_mut_data_unchecked())? };
3132

3233
if account.is_frozen() {
@@ -35,6 +36,7 @@ pub fn process_set_authority(accounts: &[AccountInfo], instruction_data: &[u8])
3536

3637
match authority_type {
3738
AuthorityType::AccountOwner => {
39+
// TODO: Can account and authority be the same?
3840
validate_owner(&account.owner, authority_info, remaning)?;
3941

4042
if let Some(authority) = new_authority {
@@ -52,6 +54,7 @@ pub fn process_set_authority(accounts: &[AccountInfo], instruction_data: &[u8])
5254
}
5355
AuthorityType::CloseAccount => {
5456
let authority = account.close_authority().unwrap_or(&account.owner);
57+
// TODO: Can account and authority be the same?
5558
validate_owner(authority, authority_info, remaning)?;
5659

5760
if let Some(authority) = new_authority {
@@ -65,14 +68,15 @@ pub fn process_set_authority(accounts: &[AccountInfo], instruction_data: &[u8])
6568
}
6669
}
6770
} else if account_info.data_len() == Mint::LEN {
71+
// SAFETY: there are no other active borrows of the `account` data.
6872
let mint = unsafe { load_mut::<Mint>(account_info.borrow_mut_data_unchecked())? };
6973

7074
match authority_type {
7175
AuthorityType::MintTokens => {
7276
// Once a mint's supply is fixed, it cannot be undone by setting a new
7377
// mint_authority.
7478
let mint_authority = mint.mint_authority().ok_or(TokenError::FixedSupply)?;
75-
79+
// TODO: Can account and authority be the same?
7680
validate_owner(mint_authority, authority_info, remaning)?;
7781

7882
if let Some(authority) = new_authority {
@@ -87,7 +91,7 @@ pub fn process_set_authority(accounts: &[AccountInfo], instruction_data: &[u8])
8791
let freeze_authority = mint
8892
.freeze_authority()
8993
.ok_or(TokenError::MintCannotFreeze)?;
90-
94+
// TODO: Can account and authority be the same?
9195
validate_owner(freeze_authority, authority_info, remaning)?;
9296

9397
if let Some(authority) = new_authority {

0 commit comments

Comments
 (0)