1#![cfg(feature = "agave-unstable-api")]
2#![allow(clippy::arithmetic_side_effects)]
3
4use {
5 solana_account::{Account, AccountSharedData, ReadableAccount},
6 solana_loader_v3_interface::{get_program_data_address, state::UpgradeableLoaderState},
7 solana_pubkey::Pubkey,
8 solana_rent::Rent,
9 solana_sdk_ids::{bpf_loader, bpf_loader_upgradeable},
10};
11
12mod spl_memo_1_0 {
13 solana_pubkey::declare_id!("Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo");
14}
15mod spl_memo_3_0 {
16 solana_pubkey::declare_id!("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr");
17}
18mod spl_memo_4_0 {
19 solana_pubkey::declare_id!("Memo4c2pN8afCj432Lb7RMVKi9PbQnnW7ewFFaV3oAH");
20}
21
22static SPL_PROGRAMS: &[(Pubkey, Pubkey, &[u8])] = &[
23 (
24 spl_generic_token::token::ID,
25 solana_sdk_ids::bpf_loader_upgradeable::ID,
26 include_bytes!("programs/spl_p_token-1.0.0.so"),
27 ),
28 (
29 spl_generic_token::token_2022::ID,
30 solana_sdk_ids::bpf_loader_upgradeable::ID,
31 include_bytes!("programs/spl_token_2022-10.0.0.so"),
32 ),
33 (
34 spl_memo_1_0::ID,
35 solana_sdk_ids::bpf_loader::ID,
36 include_bytes!("programs/spl_memo-1.0.0.so"),
37 ),
38 (
39 spl_memo_3_0::ID,
40 solana_sdk_ids::bpf_loader::ID,
41 include_bytes!("programs/spl_memo-3.0.0.so"),
42 ),
43 (
44 spl_memo_4_0::ID,
45 solana_sdk_ids::bpf_loader_upgradeable::ID,
46 include_bytes!("programs/spl_memo-4.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-5.1.0.so"),
79 ),
80 ];
82
83pub fn bpf_loader_program_account(
87 program_id: &Pubkey,
88 elf: &[u8],
89 rent: &Rent,
90) -> (Pubkey, Account) {
91 (
92 *program_id,
93 Account {
94 lamports: rent.minimum_balance(elf.len()).max(1),
95 data: elf.to_vec(),
96 owner: bpf_loader::id(),
97 executable: true,
98 rent_epoch: u64::MAX,
99 },
100 )
101}
102
103pub fn bpf_loader_upgradeable_program_accounts(
110 program_id: &Pubkey,
111 elf: &[u8],
112 upgrade_authority_address: &Pubkey,
113 rent: &Rent,
114) -> [(Pubkey, Account); 2] {
115 let programdata_address = get_program_data_address(program_id);
116 let program_account = {
117 let space = UpgradeableLoaderState::size_of_program();
118 let lamports = rent.minimum_balance(space);
119 let data = wincode::serialize(&UpgradeableLoaderState::Program {
120 programdata_address,
121 })
122 .unwrap();
123 Account {
124 lamports,
125 data,
126 owner: bpf_loader_upgradeable::id(),
127 executable: true,
128 rent_epoch: u64::MAX,
129 }
130 };
131 let programdata_account = {
132 let space = UpgradeableLoaderState::size_of_programdata_metadata() + elf.len();
133 let lamports = rent.minimum_balance(space);
134 let mut data = wincode::serialize(&UpgradeableLoaderState::ProgramData {
135 slot: 0,
136 upgrade_authority_address: Some(*upgrade_authority_address),
137 })
138 .unwrap();
139 data.extend_from_slice(elf);
140 Account {
141 lamports,
142 data,
143 owner: bpf_loader_upgradeable::id(),
144 executable: false,
145 rent_epoch: u64::MAX,
146 }
147 };
148 [
149 (*program_id, program_account),
150 (programdata_address, programdata_account),
151 ]
152}
153
154pub fn spl_programs(rent: &Rent) -> Vec<(Pubkey, AccountSharedData)> {
155 SPL_PROGRAMS
156 .iter()
157 .flat_map(|(program_id, loader_id, elf)| {
158 let mut accounts = vec![];
159 if loader_id.eq(&solana_sdk_ids::bpf_loader_upgradeable::ID) {
160 for (key, account) in bpf_loader_upgradeable_program_accounts(
161 program_id,
162 elf,
163 &Pubkey::default(),
164 rent,
165 ) {
166 accounts.push((key, AccountSharedData::from(account)));
167 }
168 } else {
169 let (key, account) = bpf_loader_program_account(program_id, elf, rent);
170 accounts.push((key, AccountSharedData::from(account)));
171 }
172 accounts
173 })
174 .collect()
175}
176
177pub fn core_bpf_programs<F>(rent: &Rent, is_feature_active: F) -> Vec<(Pubkey, AccountSharedData)>
178where
179 F: Fn(&Pubkey) -> bool,
180{
181 CORE_BPF_PROGRAMS
182 .iter()
183 .flat_map(|(program_id, feature_id, elf)| {
184 let mut accounts = vec![];
185 if feature_id.is_none() || feature_id.is_some_and(|f| is_feature_active(&f)) {
186 for (key, account) in bpf_loader_upgradeable_program_accounts(
187 program_id,
188 elf,
189 &Pubkey::default(),
190 rent,
191 ) {
192 accounts.push((key, AccountSharedData::from(account)));
193 }
194 }
195 accounts
196 })
197 .collect()
198}
199
200pub fn by_id(program_id: &Pubkey, rent: &Rent) -> Option<Vec<(Pubkey, AccountSharedData)>> {
201 let programs = spl_programs(rent);
202 if let Some(i) = programs.iter().position(|(key, _)| key == program_id) {
203 let n = num_accounts(programs[i].1.owner());
204 return Some(programs.into_iter().skip(i).take(n).collect());
205 }
206
207 let programs = core_bpf_programs(rent, |_| true);
208 if let Some(i) = programs.iter().position(|(key, _)| key == program_id) {
209 let n = num_accounts(programs[i].1.owner());
210 return Some(programs.into_iter().skip(i).take(n).collect());
211 }
212
213 None
214}
215
216fn num_accounts(owner_id: &Pubkey) -> usize {
217 if *owner_id == bpf_loader_upgradeable::id() {
218 2
219 } else {
220 1
221 }
222}