1#![cfg_attr(
2 not(feature = "agave-unstable-api"),
3 deprecated(
4 since = "3.1.0",
5 note = "This crate has been marked for formal inclusion in the Agave Unstable API. From \
6 v4.0.0 onward, the `agave-unstable-api` crate feature must be specified to \
7 acknowledge use of an interface that may break without warning."
8 )
9)]
10#![allow(clippy::arithmetic_side_effects)]
11
12use {
13 solana_account::{Account, AccountSharedData, ReadableAccount},
14 solana_loader_v3_interface::{get_program_data_address, state::UpgradeableLoaderState},
15 solana_pubkey::Pubkey,
16 solana_rent::Rent,
17 solana_sdk_ids::{bpf_loader, bpf_loader_upgradeable},
18};
19
20mod spl_memo_1_0 {
21 solana_pubkey::declare_id!("Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo");
22}
23mod spl_memo_3_0 {
24 solana_pubkey::declare_id!("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr");
25}
26
27static SPL_PROGRAMS: &[(Pubkey, Pubkey, &[u8])] = &[
28 (
29 spl_generic_token::token::ID,
30 solana_sdk_ids::bpf_loader::ID,
31 include_bytes!("programs/spl_token-3.5.0.so"),
32 ),
33 (
34 spl_generic_token::token_2022::ID,
35 solana_sdk_ids::bpf_loader_upgradeable::ID,
36 include_bytes!("programs/spl_token_2022-10.0.0.so"),
37 ),
38 (
39 spl_memo_1_0::ID,
40 solana_sdk_ids::bpf_loader::ID,
41 include_bytes!("programs/spl_memo-1.0.0.so"),
42 ),
43 (
44 spl_memo_3_0::ID,
45 solana_sdk_ids::bpf_loader::ID,
46 include_bytes!("programs/spl_memo-3.0.0.so"),
47 ),
48 (
49 spl_generic_token::associated_token_account::ID,
50 solana_sdk_ids::bpf_loader::ID,
51 include_bytes!("programs/spl_associated_token_account-1.1.1.so"),
52 ),
53];
54
55static CORE_BPF_PROGRAMS: &[(Pubkey, Option<Pubkey>, &[u8])] = &[
60 (
61 solana_sdk_ids::address_lookup_table::ID,
62 None,
63 include_bytes!("programs/core_bpf_address_lookup_table-3.0.0.so"),
64 ),
65 (
66 solana_sdk_ids::config::ID,
67 None,
68 include_bytes!("programs/core_bpf_config-3.0.0.so"),
69 ),
70 (
71 solana_sdk_ids::feature::ID,
72 None,
73 include_bytes!("programs/core_bpf_feature_gate-0.0.1.so"),
74 ),
75 (
76 solana_sdk_ids::stake::ID,
77 None,
78 include_bytes!("programs/core_bpf_stake-1.0.1.so"),
79 ),
80 ];
82
83fn bpf_loader_program_account(program_id: &Pubkey, elf: &[u8], rent: &Rent) -> (Pubkey, Account) {
87 (
88 *program_id,
89 Account {
90 lamports: rent.minimum_balance(elf.len()).max(1),
91 data: elf.to_vec(),
92 owner: bpf_loader::id(),
93 executable: true,
94 rent_epoch: u64::MAX,
95 },
96 )
97}
98
99pub fn bpf_loader_upgradeable_program_accounts(
106 program_id: &Pubkey,
107 elf: &[u8],
108 rent: &Rent,
109) -> [(Pubkey, Account); 2] {
110 let programdata_address = get_program_data_address(program_id);
111 let program_account = {
112 let space = UpgradeableLoaderState::size_of_program();
113 let lamports = rent.minimum_balance(space);
114 let data = bincode::serialize(&UpgradeableLoaderState::Program {
115 programdata_address,
116 })
117 .unwrap();
118 Account {
119 lamports,
120 data,
121 owner: bpf_loader_upgradeable::id(),
122 executable: true,
123 rent_epoch: u64::MAX,
124 }
125 };
126 let programdata_account = {
127 let space = UpgradeableLoaderState::size_of_programdata_metadata() + elf.len();
128 let lamports = rent.minimum_balance(space);
129 let mut data = bincode::serialize(&UpgradeableLoaderState::ProgramData {
130 slot: 0,
131 upgrade_authority_address: Some(Pubkey::default()),
132 })
133 .unwrap();
134 data.extend_from_slice(elf);
135 Account {
136 lamports,
137 data,
138 owner: bpf_loader_upgradeable::id(),
139 executable: false,
140 rent_epoch: u64::MAX,
141 }
142 };
143 [
144 (*program_id, program_account),
145 (programdata_address, programdata_account),
146 ]
147}
148
149pub fn spl_programs(rent: &Rent) -> Vec<(Pubkey, AccountSharedData)> {
150 SPL_PROGRAMS
151 .iter()
152 .flat_map(|(program_id, loader_id, elf)| {
153 let mut accounts = vec![];
154 if loader_id.eq(&solana_sdk_ids::bpf_loader_upgradeable::ID) {
155 for (key, account) in bpf_loader_upgradeable_program_accounts(program_id, elf, rent)
156 {
157 accounts.push((key, AccountSharedData::from(account)));
158 }
159 } else {
160 let (key, account) = bpf_loader_program_account(program_id, elf, rent);
161 accounts.push((key, AccountSharedData::from(account)));
162 }
163 accounts
164 })
165 .collect()
166}
167
168pub fn core_bpf_programs<F>(rent: &Rent, is_feature_active: F) -> Vec<(Pubkey, AccountSharedData)>
169where
170 F: Fn(&Pubkey) -> bool,
171{
172 CORE_BPF_PROGRAMS
173 .iter()
174 .flat_map(|(program_id, feature_id, elf)| {
175 let mut accounts = vec![];
176 if feature_id.is_none() || feature_id.is_some_and(|f| is_feature_active(&f)) {
177 for (key, account) in bpf_loader_upgradeable_program_accounts(program_id, elf, rent)
178 {
179 accounts.push((key, AccountSharedData::from(account)));
180 }
181 }
182 accounts
183 })
184 .collect()
185}
186
187pub fn by_id(program_id: &Pubkey, rent: &Rent) -> Option<Vec<(Pubkey, AccountSharedData)>> {
188 let programs = spl_programs(rent);
189 if let Some(i) = programs.iter().position(|(key, _)| key == program_id) {
190 let n = num_accounts(programs[i].1.owner());
191 return Some(programs.into_iter().skip(i).take(n).collect());
192 }
193
194 let programs = core_bpf_programs(rent, |_| true);
195 if let Some(i) = programs.iter().position(|(key, _)| key == program_id) {
196 let n = num_accounts(programs[i].1.owner());
197 return Some(programs.into_iter().skip(i).take(n).collect());
198 }
199
200 None
201}
202
203fn num_accounts(owner_id: &Pubkey) -> usize {
204 if *owner_id == bpf_loader_upgradeable::id() {
205 2
206 } else {
207 1
208 }
209}