1use {
4 agave_feature_set::FeatureSet,
5 agave_syscalls::create_program_runtime_environment_v1,
6 solana_account::Account,
7 solana_compute_budget::compute_budget::ComputeBudget,
8 solana_loader_v3_interface::state::UpgradeableLoaderState,
9 solana_loader_v4_interface::state::{LoaderV4State, LoaderV4Status},
10 solana_program_runtime::{
11 invoke_context::{BuiltinFunctionWithContext, InvokeContext},
12 loaded_programs::{LoadProgramMetrics, ProgramCacheEntry, ProgramCacheForTxBatch},
13 solana_sbpf::program::BuiltinProgram,
14 },
15 solana_pubkey::Pubkey,
16 solana_rent::Rent,
17 std::{
18 cell::{RefCell, RefMut},
19 collections::HashMap,
20 rc::Rc,
21 sync::Arc,
22 },
23};
24
25pub mod loader_keys {
27 pub use solana_sdk_ids::{
28 bpf_loader::ID as LOADER_V2, bpf_loader_deprecated::ID as LOADER_V1,
29 bpf_loader_upgradeable::ID as LOADER_V3, loader_v4::ID as LOADER_V4,
30 native_loader::ID as NATIVE_LOADER,
31 };
32}
33
34#[cfg(feature = "precompiles")]
35pub mod precompile_keys {
36 use solana_pubkey::Pubkey;
37 pub use solana_sdk_ids::{
38 ed25519_program::ID as ED25519_PROGRAM, secp256k1_program::ID as SECP256K1_PROGRAM,
39 secp256r1_program::ID as SECP256R1_PROGRAM,
40 };
41
42 pub(crate) fn is_precompile(program_id: &Pubkey) -> bool {
43 matches!(
44 *program_id,
45 ED25519_PROGRAM | SECP256K1_PROGRAM | SECP256R1_PROGRAM
46 )
47 }
48}
49
50#[cfg(not(feature = "precompiles"))]
51pub mod precompile_keys {
52 use solana_pubkey::Pubkey;
53
54 pub(crate) const fn is_precompile(_program_id: &Pubkey) -> bool {
55 false
56 }
57}
58
59pub struct CacheEntry {
60 pub loader_key: Pubkey,
61 pub elf_bytes: Option<Vec<u8>>,
62}
63
64pub struct ProgramCache {
65 cache: Rc<RefCell<ProgramCacheForTxBatch>>,
66 entries_cache: Rc<RefCell<HashMap<Pubkey, CacheEntry>>>,
76 pub program_runtime_environment: BuiltinProgram<InvokeContext<'static, 'static>>,
79}
80
81impl ProgramCache {
82 pub fn new(
83 feature_set: &FeatureSet,
84 compute_budget: &ComputeBudget,
85 enable_register_tracing: bool,
86 ) -> Self {
87 let me = Self {
88 cache: Rc::new(RefCell::new(ProgramCacheForTxBatch::default())),
89 entries_cache: Rc::new(RefCell::new(HashMap::new())),
90 program_runtime_environment: create_program_runtime_environment_v1(
91 &feature_set.runtime_features(),
92 &compute_budget.to_budget(),
93 false,
94 enable_register_tracing,
95 )
96 .unwrap(),
97 };
98 BUILTINS.iter().for_each(|builtin| {
99 let program_id = builtin.program_id;
100 let entry = builtin.program_cache_entry();
101 me.replenish(program_id, entry, None);
102 });
103 me
104 }
105
106 pub(crate) fn cache(&self) -> RefMut<'_, ProgramCacheForTxBatch> {
107 self.cache.borrow_mut()
108 }
109
110 fn replenish(
111 &self,
112 program_id: Pubkey,
113 entry: Arc<ProgramCacheEntry>,
114 elf_bytes: Option<&[u8]>,
115 ) {
116 self.entries_cache.borrow_mut().insert(
117 program_id,
118 CacheEntry {
119 loader_key: entry.account_owner(),
120 elf_bytes: elf_bytes.map(|s| s.to_vec()),
121 },
122 );
123 self.cache.borrow_mut().replenish(program_id, entry);
124 }
125
126 pub fn add_builtin(&mut self, builtin: Builtin) {
128 let program_id = builtin.program_id;
129 let entry = builtin.program_cache_entry();
130 self.replenish(program_id, entry, None);
131 }
132
133 pub fn add_program(&mut self, program_id: &Pubkey, loader_key: &Pubkey, elf: &[u8]) {
135 let environment = {
138 let config = self.program_runtime_environment.get_config().clone();
139 let mut loader = BuiltinProgram::new_loader(config);
140
141 for (_key, (name, value)) in self
142 .program_runtime_environment
143 .get_function_registry()
144 .iter()
145 {
146 let name = std::str::from_utf8(name).unwrap();
147 loader.register_function(name, value).unwrap();
148 }
149
150 Arc::new(loader)
151 };
152 self.replenish(
153 *program_id,
154 Arc::new(
155 ProgramCacheEntry::new(
156 loader_key,
157 environment,
158 0,
159 0,
160 elf,
161 elf.len(),
162 &mut LoadProgramMetrics::default(),
163 )
164 .unwrap(),
165 ),
166 Some(elf),
167 );
168 }
169
170 pub fn load_program(&self, program_id: &Pubkey) -> Option<Arc<ProgramCacheEntry>> {
172 self.cache.borrow().find(program_id)
173 }
174
175 pub(crate) fn get_all_keyed_program_accounts(&self) -> Vec<(Pubkey, Account)> {
179 self.entries_cache
180 .borrow()
181 .iter()
182 .map(|(program_id, cache_entry)| match cache_entry.loader_key {
183 loader_keys::NATIVE_LOADER => {
184 create_keyed_account_for_builtin_program(program_id, "I'm a stub!")
185 }
186 loader_keys::LOADER_V1 => (*program_id, create_program_account_loader_v1(&[])),
187 loader_keys::LOADER_V2 => (*program_id, create_program_account_loader_v2(&[])),
188 loader_keys::LOADER_V3 => {
189 (*program_id, create_program_account_loader_v3(program_id))
190 }
191 loader_keys::LOADER_V4 => (*program_id, create_program_account_loader_v4(&[])),
192 _ => panic!("Invalid loader key: {}", cache_entry.loader_key),
193 })
194 .collect()
195 }
196
197 pub(crate) fn maybe_create_program_account(&self, pubkey: &Pubkey) -> Option<Account> {
198 self.entries_cache
201 .borrow()
202 .get(pubkey)
203 .map(|cache_entry| match cache_entry.loader_key {
204 loader_keys::NATIVE_LOADER => {
205 create_keyed_account_for_builtin_program(pubkey, "I'm a stub!").1
206 }
207 loader_keys::LOADER_V1 => create_program_account_loader_v1(&[]),
208 loader_keys::LOADER_V2 => create_program_account_loader_v2(&[]),
209 loader_keys::LOADER_V3 => create_program_account_loader_v3(pubkey),
210 loader_keys::LOADER_V4 => create_program_account_loader_v4(&[]),
211 _ => panic!("Invalid loader key: {}", cache_entry.loader_key),
212 })
213 }
214
215 pub fn get_program_elf_bytes(&self, program_id: &Pubkey) -> Option<Vec<u8>> {
216 match self.entries_cache.borrow().get(program_id) {
217 None => None,
218 Some(cache_entry) => cache_entry.elf_bytes.to_owned(),
219 }
220 }
221}
222
223pub struct Builtin {
224 pub program_id: Pubkey,
225 pub name: &'static str,
226 pub entrypoint: BuiltinFunctionWithContext,
227}
228
229impl Builtin {
230 fn program_cache_entry(&self) -> Arc<ProgramCacheEntry> {
231 Arc::new(ProgramCacheEntry::new_builtin(
232 0,
233 self.name.len(),
234 self.entrypoint,
235 ))
236 }
237}
238
239static BUILTINS: &[Builtin] = &[
240 Builtin {
241 program_id: solana_system_program::id(),
242 name: "system_program",
243 entrypoint: solana_system_program::system_processor::Entrypoint::vm,
244 },
245 Builtin {
246 program_id: loader_keys::LOADER_V2,
247 name: "solana_bpf_loader_program",
248 entrypoint: solana_bpf_loader_program::Entrypoint::vm,
249 },
250 Builtin {
251 program_id: loader_keys::LOADER_V3,
252 name: "solana_bpf_loader_upgradeable_program",
253 entrypoint: solana_bpf_loader_program::Entrypoint::vm,
254 },
255 #[cfg(feature = "all-builtins")]
256 Builtin {
257 program_id: loader_keys::LOADER_V1,
258 name: "solana_bpf_loader_deprecated_program",
259 entrypoint: solana_bpf_loader_program::Entrypoint::vm,
260 },
261 #[cfg(feature = "all-builtins")]
262 Builtin {
263 program_id: loader_keys::LOADER_V4,
264 name: "solana_loader_v4_program",
265 entrypoint: solana_loader_v4_program::Entrypoint::vm,
266 },
267 #[cfg(feature = "all-builtins")]
268 Builtin {
269 program_id: solana_sdk_ids::zk_elgamal_proof_program::id(),
270 name: "zk_elgamal_proof_program",
271 entrypoint: solana_zk_elgamal_proof_program::Entrypoint::vm,
272 },
273 #[cfg(feature = "all-builtins")]
274 Builtin {
275 program_id: solana_sdk_ids::compute_budget::id(),
276 name: "compute_budget_program",
277 entrypoint: solana_compute_budget_program::Entrypoint::vm,
278 },
279 #[cfg(feature = "all-builtins")]
280 Builtin {
281 program_id: solana_sdk_ids::vote::id(),
282 name: "vote_program",
283 entrypoint: solana_vote_program::vote_processor::Entrypoint::vm,
284 },
285];
286
287pub fn create_keyed_account_for_builtin_program(
289 program_id: &Pubkey,
290 name: &str,
291) -> (Pubkey, Account) {
292 let data = name.as_bytes().to_vec();
293 let lamports = Rent::default().minimum_balance(data.len());
294 let account = Account {
295 lamports,
296 data,
297 owner: loader_keys::NATIVE_LOADER,
298 executable: true,
299 ..Default::default()
300 };
301 (*program_id, account)
302}
303
304pub fn keyed_account_for_system_program() -> (Pubkey, Account) {
306 create_keyed_account_for_builtin_program(&BUILTINS[0].program_id, BUILTINS[0].name)
307}
308
309pub fn keyed_account_for_bpf_loader_v2_program() -> (Pubkey, Account) {
311 create_keyed_account_for_builtin_program(&BUILTINS[1].program_id, BUILTINS[1].name)
312}
313
314pub fn keyed_account_for_bpf_loader_v3_program() -> (Pubkey, Account) {
316 create_keyed_account_for_builtin_program(&BUILTINS[2].program_id, BUILTINS[2].name)
317}
318
319pub fn create_program_account_loader_v1(elf: &[u8]) -> Account {
323 let lamports = Rent::default().minimum_balance(elf.len());
324 Account {
325 lamports,
326 data: elf.to_vec(),
327 owner: loader_keys::LOADER_V1,
328 executable: true,
329 ..Default::default()
330 }
331}
332
333pub fn create_program_account_loader_v2(elf: &[u8]) -> Account {
335 let lamports = Rent::default().minimum_balance(elf.len());
336 Account {
337 lamports,
338 data: elf.to_vec(),
339 owner: loader_keys::LOADER_V2,
340 executable: true,
341 ..Default::default()
342 }
343}
344
345pub fn create_program_account_loader_v3(program_id: &Pubkey) -> Account {
347 let programdata_address =
348 Pubkey::find_program_address(&[program_id.as_ref()], &loader_keys::LOADER_V3).0;
349 let data = bincode::serialize(&UpgradeableLoaderState::Program {
350 programdata_address,
351 })
352 .unwrap();
353 let lamports = Rent::default().minimum_balance(data.len());
354 Account {
355 lamports,
356 data,
357 owner: loader_keys::LOADER_V3,
358 executable: true,
359 ..Default::default()
360 }
361}
362
363pub fn create_program_data_account_loader_v3(elf: &[u8]) -> Account {
365 let data = {
366 let elf_offset = UpgradeableLoaderState::size_of_programdata_metadata();
367 let data_len = elf_offset + elf.len();
368 let mut data = vec![0; data_len];
369 bincode::serialize_into(
370 &mut data[0..elf_offset],
371 &UpgradeableLoaderState::ProgramData {
372 slot: 0,
373 upgrade_authority_address: None,
374 },
375 )
376 .unwrap();
377 data[elf_offset..].copy_from_slice(elf);
378 data
379 };
380 let lamports = Rent::default().minimum_balance(data.len());
381 Account {
382 lamports,
383 data,
384 owner: loader_keys::LOADER_V3,
385 executable: false,
386 ..Default::default()
387 }
388}
389
390pub fn create_program_account_pair_loader_v3(
395 program_id: &Pubkey,
396 elf: &[u8],
397) -> (Account, Account) {
398 (
399 create_program_account_loader_v3(program_id),
400 create_program_data_account_loader_v3(elf),
401 )
402}
403
404pub fn create_program_account_loader_v4(elf: &[u8]) -> Account {
406 let data = unsafe {
407 let elf_offset = LoaderV4State::program_data_offset();
408 let data_len = elf_offset + elf.len();
409 let mut data = vec![0u8; data_len];
410 *std::mem::transmute::<&mut [u8; LoaderV4State::program_data_offset()], &mut LoaderV4State>(
411 (&mut data[0..elf_offset]).try_into().unwrap(),
412 ) = LoaderV4State {
413 slot: 0,
414 authority_address_or_next_version: Pubkey::new_from_array([2; 32]),
415 status: LoaderV4Status::Deployed,
416 };
417 data[elf_offset..].copy_from_slice(elf);
418 data
419 };
420 let lamports = Rent::default().minimum_balance(data.len());
421 Account {
422 lamports,
423 data,
424 owner: loader_keys::LOADER_V4,
425 executable: true,
426 ..Default::default()
427 }
428}