Skip to content

Commit dbcab2e

Browse files
febozubayr1
authored andcommitted
parent 5af8a54
author Fernando Otero <[email protected]> 1752707789 +0100 committer zubayr1 <[email protected]> 1752784793 +0200 Clarify the use of constant values (anza-xyz#200) * Add comments on constants * Improve offset comments * Add bitmask to dictionary * Renamed to field_at_offset Ignore `zero_init` parameter (anza-xyz#203) Ignore zero_init parameter issue 136 idea solution add checked accounts fix instruction_data argument for lamports use Sysvar rent account update mod fix typo issue 136 idea solution fix instruction_data argument for lamports update mod fix typo implement invoke_signed_checked
1 parent 5af8a54 commit dbcab2e

File tree

7 files changed

+347
-252
lines changed

7 files changed

+347
-252
lines changed
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
use pinocchio::{
2+
account_info::AccountInfo,
3+
instruction::{AccountMeta, Instruction, Signer},
4+
program::invoke_signed,
5+
pubkey::Pubkey,
6+
sysvars::{rent::Rent, Sysvar},
7+
ProgramResult,
8+
program_error::ProgramError,
9+
};
10+
11+
/// Create a new account.
12+
///
13+
/// ### Accounts:
14+
/// 0. `[WRITE, SIGNER]` Funding account
15+
/// 1. `[WRITE, SIGNER]` New account
16+
pub struct CreateAccountChecked<'a> {
17+
/// Funding account.
18+
pub from: &'a AccountInfo,
19+
20+
/// New account.
21+
pub to: &'a AccountInfo,
22+
23+
/// Number of bytes of memory to allocate.
24+
pub space: u64,
25+
26+
/// Address of program that will own the new account.
27+
pub owner: &'a Pubkey,
28+
29+
/// Sysvar rent account.
30+
pub sysvar_rent_account: &'a AccountInfo,
31+
}
32+
33+
impl CreateAccountChecked<'_> {
34+
#[inline(always)]
35+
pub fn invoke(&self) -> ProgramResult {
36+
self.invoke_signed_checked(&[])
37+
}
38+
39+
#[inline(always)]
40+
pub fn invoke_signed_checked(&self, signers: &[Signer]) -> ProgramResult {
41+
// Get lamports from rent
42+
let rent = Rent::from_account_info(self.sysvar_rent_account)?;
43+
let lamports = rent.minimum_balance(self.space as usize);
44+
45+
// Check if the funding account has enough lamports
46+
if self.from.lamports() < lamports {
47+
return Err(ProgramError::InsufficientFunds);
48+
}
49+
50+
// Check if the new account is already initialized
51+
if !self.to.data_is_empty() {
52+
return Err(ProgramError::InvalidAccountData);
53+
}
54+
55+
self.invoke_signed(signers, lamports)
56+
}
57+
58+
#[inline(always)]
59+
fn invoke_signed(&self, signers: &[Signer], lamports: u64) -> ProgramResult {
60+
// account metadata
61+
let account_metas: [AccountMeta; 2] = [
62+
AccountMeta::writable_signer(self.from.key()),
63+
AccountMeta::writable_signer(self.to.key()),
64+
];
65+
66+
// instruction data
67+
// - [0..4 ]: instruction discriminator
68+
// - [4..12 ]: lamports
69+
// - [12..20]: account space
70+
// - [20..52]: owner pubkey
71+
let mut instruction_data = [0; 52];
72+
// create account instruction has a '0' discriminator
73+
instruction_data[4..12].copy_from_slice(&lamports.to_le_bytes());
74+
instruction_data[12..20].copy_from_slice(&self.space.to_le_bytes());
75+
instruction_data[20..52].copy_from_slice(self.owner.as_ref());
76+
77+
let instruction = Instruction {
78+
program_id: &crate::ID,
79+
accounts: &account_metas,
80+
data: &instruction_data,
81+
};
82+
83+
invoke_signed(&instruction, &[self.from, self.to], signers)
84+
}
85+
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
use pinocchio::{
2+
account_info::AccountInfo,
3+
instruction::{AccountMeta, Instruction, Signer},
4+
program::invoke_signed,
5+
pubkey::{self, Pubkey, MAX_SEED_LEN},
6+
sysvars::{rent::Rent, Sysvar},
7+
ProgramResult,
8+
program_error::ProgramError,
9+
};
10+
11+
/// Create a new account at an address derived from a base pubkey and a seed.
12+
///
13+
/// ### Accounts:
14+
/// 0. `[WRITE, SIGNER]` Funding account
15+
/// 1. `[WRITE]` Created account
16+
/// 2. `[SIGNER]` (optional) Base account; the account matching the base Pubkey below must be
17+
/// provided as a signer, but may be the same as the funding account
18+
pub struct CreateAccountWithSeedChecked<'a, 'b, 'c> {
19+
/// Funding account.
20+
pub from: &'a AccountInfo,
21+
22+
/// New account.
23+
pub to: &'a AccountInfo,
24+
25+
/// Base account.
26+
///
27+
/// The account matching the base Pubkey below must be provided as
28+
/// a signer, but may be the same as the funding account and provided
29+
/// as account 0.
30+
pub base: Option<&'a AccountInfo>,
31+
32+
/// String of ASCII chars, no longer than `Pubkey::MAX_SEED_LEN`.
33+
pub seed: &'b str,
34+
35+
/// Number of bytes of memory to allocate.
36+
pub space: u64,
37+
38+
/// Address of program that will own the new account.
39+
pub owner: &'c Pubkey,
40+
41+
/// Sysvar rent account.
42+
pub sysvar_rent_account: &'a AccountInfo,
43+
}
44+
45+
impl CreateAccountWithSeed<'_, '_, '_> {
46+
#[inline(always)]
47+
pub fn invoke(&self) -> ProgramResult {
48+
self.invoke_signed_checked(&[])
49+
}
50+
51+
#[inline(always)]
52+
pub fn invoke_signed_checked(&self, signers: &[Signer]) -> ProgramResult {
53+
// Get lamports from rent
54+
let rent = Rent::from_account_info(self.sysvar_rent_account)?;
55+
let lamports = rent.minimum_balance(self.space as usize);
56+
57+
// Check if the seed is valid
58+
if self.seed.len() > MAX_SEED_LEN {
59+
return Err(ProgramError::InvalidInstructionData);
60+
}
61+
62+
// Check if the funding account has enough lamports
63+
if self.from.lamports() < lamports {
64+
return Err(ProgramError::InsufficientFunds);
65+
}
66+
67+
// Check if the new account is already initialized
68+
if !self.to.data_is_empty() {
69+
return Err(ProgramError::InvalidAccountData);
70+
}
71+
72+
self.invoke_signed(signers, lamports)
73+
}
74+
75+
#[inline(always)]
76+
fn invoke_signed(&self, signers: &[Signer], lamports: u64) -> ProgramResult {
77+
// account metadata
78+
let account_metas: [AccountMeta; 3] = [
79+
AccountMeta::writable_signer(self.from.key()),
80+
AccountMeta::writable(self.to.key()),
81+
AccountMeta::readonly_signer(self.base.unwrap_or(self.from).key()),
82+
];
83+
84+
// instruction data
85+
// - [0..4 ]: instruction discriminator
86+
// - [4..36 ]: base pubkey
87+
// - [36..44]: seed length
88+
// - [44.. ]: seed (max 32)
89+
// - [.. +8]: lamports
90+
// - [.. +8]: account space
91+
// - [.. +32]: owner pubkey
92+
let mut instruction_data = [0; 120];
93+
instruction_data[0] = 3;
94+
instruction_data[4..36].copy_from_slice(self.base.unwrap_or(self.from).key());
95+
instruction_data[36..44].copy_from_slice(&u64::to_le_bytes(self.seed.len() as u64));
96+
97+
let offset = 44 + self.seed.len();
98+
instruction_data[44..offset].copy_from_slice(self.seed.as_bytes());
99+
instruction_data[offset..offset + 8].copy_from_slice(&lamports.to_le_bytes());
100+
instruction_data[offset + 8..offset + 16].copy_from_slice(&self.space.to_le_bytes());
101+
instruction_data[offset + 16..offset + 48].copy_from_slice(self.owner.as_ref());
102+
103+
let instruction = Instruction {
104+
program_id: &crate::ID,
105+
accounts: &account_metas,
106+
data: &instruction_data[..offset + 48],
107+
};
108+
109+
invoke_signed(
110+
&instruction,
111+
&[self.from, self.to, self.base.unwrap_or(self.from)],
112+
signers,
113+
)
114+
}
115+
}

programs/system/src/instructions/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ mod assign;
55
mod assign_with_seed;
66
mod authorize_nonce_account;
77
mod create_account;
8+
mod create_account_checked;
89
mod create_account_with_seed;
10+
mod create_account_with_seed_checked;
911
mod initialize_nonce_account;
1012
mod transfer;
1113
mod transfer_with_seed;
@@ -19,7 +21,9 @@ pub use assign::*;
1921
pub use assign_with_seed::*;
2022
pub use authorize_nonce_account::*;
2123
pub use create_account::*;
24+
pub use create_account_checked::*;
2225
pub use create_account_with_seed::*;
26+
pub use create_account_with_seed_checked::*;
2327
pub use initialize_nonce_account::*;
2428
pub use transfer::*;
2529
pub use transfer_with_seed::*;

scripts/setup/solana.dic

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,5 @@ callee
6363
RPC
6464
ed25519
6565
performant
66-
syscall/S
66+
syscall/S
67+
bitmask

0 commit comments

Comments
 (0)