1#![cfg(feature = "agave-unstable-api")]
2#![deny(clippy::arithmetic_side_effects)]
3#![deny(clippy::indexing_slicing)]
4
5#[cfg(feature = "svm-internal")]
6use qualifier_attr::qualifiers;
7use {
8 solana_bincode::limited_deserialize,
9 solana_instruction::AccountMeta,
10 solana_instruction_error::InstructionError,
11 solana_loader_v3_interface::{
12 instruction::{MINIMUM_EXTEND_PROGRAM_BYTES, UpgradeableLoaderInstruction},
13 state::UpgradeableLoaderState,
14 },
15 solana_program_runtime::{
16 deploy_program,
17 invoke_context::InvokeContext,
18 program_cache_entry::{ProgramCacheEntry, ProgramCacheEntryOwner, ProgramCacheEntryType},
19 sysvar_cache::get_sysvar_with_account_check,
20 vm::execute,
21 },
22 solana_pubkey::Pubkey,
23 solana_sbpf::{declare_builtin_function, elf::get_sbpf_version, program::SBPFVersion},
24 solana_sdk_ids::{bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable, native_loader},
25 solana_svm_log_collector::{LogCollector, ic_logger_msg},
26 solana_svm_measure::measure::Measure,
27 solana_svm_type_overrides::sync::Arc,
28 solana_system_interface::{MAX_PERMITTED_DATA_LENGTH, instruction as system_instruction},
29 solana_transaction_context::{IndexOfAccount, instruction::InstructionContext},
30 std::{cell::RefCell, rc::Rc},
31 wincode::SchemaRead,
32};
33
34#[cfg_attr(feature = "svm-internal", qualifiers(pub))]
35const DEFAULT_LOADER_COMPUTE_UNITS: u64 = 570;
36#[cfg_attr(feature = "svm-internal", qualifiers(pub))]
37const DEPRECATED_LOADER_COMPUTE_UNITS: u64 = 1_140;
38#[cfg_attr(feature = "svm-internal", qualifiers(pub))]
39const UPGRADEABLE_LOADER_COMPUTE_UNITS: u64 = 2_370;
40
41#[cfg_attr(feature = "svm-internal", qualifiers(pub))]
43const WRITE_INSTRUCTION_TAG: [u8; 4] = 1u32.to_le_bytes();
44
45#[cfg_attr(feature = "svm-internal", qualifiers(pub))]
47const WRITE_INSTRUCTION_HEADER_LEN: usize = 16;
48
49fn parse_write_instruction(
50 instruction_data: &[u8],
51 unbound_loader_v3_instruction_data: bool,
52) -> Result<(u32, &[u8]), InstructionError> {
53 #[derive(SchemaRead)]
54 struct WriteInstruction<'a> {
55 tag: u32,
56 offset: u32,
57 bytes: &'a [u8],
58 }
59
60 let WriteInstruction { tag, offset, bytes } = wincode::deserialize(instruction_data)
61 .map_err(|_| InstructionError::InvalidInstructionData)?;
62
63 if tag.to_le_bytes() != WRITE_INSTRUCTION_TAG {
64 return Err(InstructionError::InvalidInstructionData);
66 }
67
68 if !unbound_loader_v3_instruction_data
69 && WRITE_INSTRUCTION_HEADER_LEN.saturating_add(bytes.len())
70 > solana_packet::PACKET_DATA_SIZE
71 {
72 return Err(InstructionError::InvalidInstructionData);
73 }
74
75 Ok((offset, bytes))
76}
77
78fn write_program_data(
79 program_data_offset: usize,
80 bytes: &[u8],
81 instruction_context: &InstructionContext,
82 log_collector: &Option<Rc<RefCell<LogCollector>>>,
83) -> Result<(), InstructionError> {
84 let mut program = instruction_context.try_borrow_instruction_account(0)?;
85 let data = program.get_data_mut()?;
86 let write_offset = program_data_offset.saturating_add(bytes.len());
87 if data.len() < write_offset {
88 ic_logger_msg!(
89 log_collector,
90 "Write overflow: {} < {}",
91 data.len(),
92 write_offset,
93 );
94 return Err(InstructionError::AccountDataTooSmall);
95 }
96 data.get_mut(program_data_offset..write_offset)
97 .ok_or(InstructionError::AccountDataTooSmall)?
98 .copy_from_slice(bytes);
99 Ok(())
100}
101
102declare_builtin_function!(
103 Entrypoint,
104 fn rust(
105 invoke_context: &mut InvokeContext<'static, 'static>,
106 _arg0: u64,
107 _arg1: u64,
108 _arg2: u64,
109 _arg3: u64,
110 _arg4: u64,
111 ) -> Result<u64, Box<dyn std::error::Error>> {
112 process_instruction_inner(invoke_context)
113 }
114);
115
116mod migration_authority {
117 solana_pubkey::declare_id!("3Scf35jMNk2xXBD6areNjgMtXgp5ZspDhms8vdcbzC42");
118}
119
120#[cfg_attr(feature = "svm-internal", qualifiers(pub))]
121pub(crate) fn process_instruction_inner<'a>(
122 invoke_context: &mut InvokeContext<'a, 'a>,
123) -> Result<u64, Box<dyn std::error::Error>> {
124 let log_collector = invoke_context.get_log_collector();
125 let transaction_context = &invoke_context.transaction_context;
126 let instruction_context = transaction_context.get_current_instruction_context()?;
127 let program_id = instruction_context.get_program_key()?;
128 let owner_id = instruction_context.get_program_owner()?;
129
130 if native_loader::check_id(&owner_id) {
132 let program_id = instruction_context.get_program_key()?;
133 return if bpf_loader_upgradeable::check_id(program_id) {
134 invoke_context.consume_checked(UPGRADEABLE_LOADER_COMPUTE_UNITS)?;
135 process_loader_upgradeable_instruction(invoke_context)
136 } else if bpf_loader::check_id(program_id) {
137 invoke_context.consume_checked(DEFAULT_LOADER_COMPUTE_UNITS)?;
138 ic_logger_msg!(
139 log_collector,
140 "BPF loader management instructions are no longer supported",
141 );
142 Err(InstructionError::UnsupportedProgramId)
143 } else if bpf_loader_deprecated::check_id(program_id) {
144 invoke_context.consume_checked(DEPRECATED_LOADER_COMPUTE_UNITS)?;
145 ic_logger_msg!(log_collector, "Deprecated loader is no longer supported");
146 Err(InstructionError::UnsupportedProgramId)
147 } else {
148 ic_logger_msg!(log_collector, "Invalid BPF loader id");
149 Err(InstructionError::UnsupportedProgramId)
150 }
151 .map(|_| 0)
152 .map_err(|error| Box::new(error) as Box<dyn std::error::Error>);
153 }
154
155 let mut get_or_create_executor_time = Measure::start("get_or_create_executor_time");
157 let executor = invoke_context
158 .program_cache_for_tx_batch
159 .find(program_id)
160 .ok_or_else(|| {
161 ic_logger_msg!(log_collector, "Program is not cached");
162 InstructionError::UnsupportedProgramId
163 })?;
164 get_or_create_executor_time.stop();
165 invoke_context.timings.get_or_create_executor_us += get_or_create_executor_time.as_us();
166
167 match &executor.program {
168 ProgramCacheEntryType::FailedVerification(_)
169 | ProgramCacheEntryType::Closed
170 | ProgramCacheEntryType::DelayVisibility => {
171 ic_logger_msg!(log_collector, "Program is not deployed");
172 Err(Box::new(InstructionError::UnsupportedProgramId) as Box<dyn std::error::Error>)
173 }
174 ProgramCacheEntryType::Loaded(executable) => execute(executable, invoke_context, &executor),
175 _ => Err(Box::new(InstructionError::UnsupportedProgramId) as Box<dyn std::error::Error>),
176 }
177 .map(|_| 0)
178}
179
180fn process_loader_upgradeable_instruction(
181 invoke_context: &mut InvokeContext,
182) -> Result<(), InstructionError> {
183 let log_collector = invoke_context.get_log_collector();
184 let transaction_context = &invoke_context.transaction_context;
185 let instruction_context = transaction_context.get_current_instruction_context()?;
186 let instruction_data = instruction_context.get_instruction_data();
187 let program_id = instruction_context.get_program_key()?;
188 let unbound_loader_v3_instruction_data = invoke_context
189 .get_feature_set()
190 .unbound_loader_v3_instruction_data;
191
192 let instruction = if instruction_data.starts_with(&WRITE_INSTRUCTION_TAG) {
193 UpgradeableLoaderInstruction::Write {
195 offset: 0,
196 bytes: Vec::new(),
197 }
198 } else if unbound_loader_v3_instruction_data {
199 wincode::deserialize(instruction_data)
200 .map_err(|_| InstructionError::InvalidInstructionData)?
201 } else {
202 limited_deserialize(instruction_data, solana_packet::PACKET_DATA_SIZE as u64)?
203 };
204
205 match instruction {
206 UpgradeableLoaderInstruction::InitializeBuffer => {
207 instruction_context.check_number_of_instruction_accounts(2)?;
208 let mut buffer = instruction_context.try_borrow_instruction_account(0)?;
209
210 if UpgradeableLoaderState::Uninitialized != buffer.get_state()? {
211 ic_logger_msg!(log_collector, "Buffer account already initialized");
212 return Err(InstructionError::AccountAlreadyInitialized);
213 }
214
215 let authority_key = Some(*instruction_context.get_key_of_instruction_account(1)?);
216
217 buffer.set_state(&UpgradeableLoaderState::Buffer {
218 authority_address: authority_key,
219 })?;
220 }
221 UpgradeableLoaderInstruction::Write { .. } => {
222 let (offset, payload) =
223 parse_write_instruction(instruction_data, unbound_loader_v3_instruction_data)?;
224 instruction_context.check_number_of_instruction_accounts(2)?;
225 let buffer = instruction_context.try_borrow_instruction_account(0)?;
226
227 if let UpgradeableLoaderState::Buffer { authority_address } = buffer.get_state()? {
228 if authority_address.is_none() {
229 ic_logger_msg!(log_collector, "Buffer is immutable");
230 return Err(InstructionError::Immutable); }
232 let authority_key = Some(*instruction_context.get_key_of_instruction_account(1)?);
233 if authority_address != authority_key {
234 ic_logger_msg!(log_collector, "Incorrect buffer authority provided");
235 return Err(InstructionError::IncorrectAuthority);
236 }
237 if !instruction_context.is_instruction_account_signer(1)? {
238 ic_logger_msg!(log_collector, "Buffer authority did not sign");
239 return Err(InstructionError::MissingRequiredSignature);
240 }
241 } else {
242 ic_logger_msg!(log_collector, "Invalid Buffer account");
243 return Err(InstructionError::InvalidAccountData);
244 }
245 drop(buffer);
246 write_program_data(
247 UpgradeableLoaderState::size_of_buffer_metadata().saturating_add(offset as usize),
248 payload,
249 &instruction_context,
250 &log_collector,
251 )?;
252 }
253 UpgradeableLoaderInstruction::DeployWithMaxDataLen { max_data_len } => {
254 instruction_context.check_number_of_instruction_accounts(4)?;
255 let payer_key = *instruction_context.get_key_of_instruction_account(0)?;
256 let programdata_key = *instruction_context.get_key_of_instruction_account(1)?;
257 let rent =
258 get_sysvar_with_account_check::rent(invoke_context, &instruction_context, 4)?;
259 let clock =
260 get_sysvar_with_account_check::clock(invoke_context, &instruction_context, 5)?;
261 instruction_context.check_number_of_instruction_accounts(8)?;
262 let authority_key = Some(*instruction_context.get_key_of_instruction_account(7)?);
263
264 let program = instruction_context.try_borrow_instruction_account(2)?;
267 if UpgradeableLoaderState::Uninitialized != program.get_state()? {
268 ic_logger_msg!(log_collector, "Program account already initialized");
269 return Err(InstructionError::AccountAlreadyInitialized);
270 }
271 if program.get_data().len() < UpgradeableLoaderState::size_of_program() {
272 ic_logger_msg!(log_collector, "Program account too small");
273 return Err(InstructionError::AccountDataTooSmall);
274 }
275 if program.get_lamports() < rent.minimum_balance(program.get_data().len()) {
276 ic_logger_msg!(log_collector, "Program account not rent-exempt");
277 return Err(InstructionError::ExecutableAccountNotRentExempt);
278 }
279 let new_program_id = *program.get_key();
280 drop(program);
281
282 let buffer = instruction_context.try_borrow_instruction_account(3)?;
285 if !buffer.is_writable() {
286 ic_logger_msg!(log_collector, "Buffer account not writeable");
287 return Err(InstructionError::InvalidArgument);
288 }
289 if buffer.get_owner() != program_id {
290 ic_logger_msg!(log_collector, "Buffer account not owned by loader");
291 return Err(InstructionError::IncorrectProgramId);
292 }
293 if let UpgradeableLoaderState::Buffer { authority_address } = buffer.get_state()? {
294 if authority_address != authority_key {
295 ic_logger_msg!(log_collector, "Buffer and upgrade authority don't match");
296 return Err(InstructionError::IncorrectAuthority);
297 }
298 if !instruction_context.is_instruction_account_signer(7)? {
299 ic_logger_msg!(log_collector, "Upgrade authority did not sign");
300 return Err(InstructionError::MissingRequiredSignature);
301 }
302 } else {
303 ic_logger_msg!(log_collector, "Invalid Buffer account");
304 return Err(InstructionError::InvalidArgument);
305 }
306 let buffer_key = *buffer.get_key();
307 let buffer_data_offset = UpgradeableLoaderState::size_of_buffer_metadata();
308 let buffer_data_len = buffer.get_data().len().saturating_sub(buffer_data_offset);
309 let programdata_data_offset = UpgradeableLoaderState::size_of_programdata_metadata();
310 let programdata_len = UpgradeableLoaderState::size_of_programdata(max_data_len);
311 if buffer.get_data().len() < UpgradeableLoaderState::size_of_buffer_metadata()
312 || buffer_data_len == 0
313 {
314 ic_logger_msg!(log_collector, "Buffer account too small");
315 return Err(InstructionError::InvalidAccountData);
316 }
317 drop(buffer);
318 if max_data_len < buffer_data_len {
319 ic_logger_msg!(
320 log_collector,
321 "Max data length is too small to hold Buffer data"
322 );
323 return Err(InstructionError::AccountDataTooSmall);
324 }
325 if programdata_len > MAX_PERMITTED_DATA_LENGTH as usize {
326 ic_logger_msg!(log_collector, "Max data length is too large");
327 return Err(InstructionError::InvalidArgument);
328 }
329
330 let (derived_address, bump_seed) =
332 Pubkey::find_program_address(&[new_program_id.as_ref()], program_id);
333 if derived_address != programdata_key {
334 ic_logger_msg!(log_collector, "ProgramData address is not derived");
335 return Err(InstructionError::InvalidArgument);
336 }
337
338 {
340 let mut buffer = instruction_context.try_borrow_instruction_account(3)?;
341 let mut payer = instruction_context.try_borrow_instruction_account(0)?;
342 payer.checked_add_lamports(buffer.get_lamports())?;
343 buffer.set_lamports(0)?;
344 }
345
346 let owner_id = *program_id;
347 let mut instruction = system_instruction::create_account(
348 &payer_key,
349 &programdata_key,
350 1.max(rent.minimum_balance(programdata_len)),
351 programdata_len as u64,
352 program_id,
353 );
354
355 instruction
357 .accounts
358 .push(AccountMeta::new(buffer_key, false));
359
360 invoke_context
361 .native_invoke_signed(instruction, &[&[new_program_id.as_ref(), &[bump_seed]]])?;
362
363 let transaction_context = &invoke_context.transaction_context;
365 let instruction_context = transaction_context.get_current_instruction_context()?;
366 let buffer = instruction_context.try_borrow_instruction_account(3)?;
367 deploy_program!(
368 invoke_context,
369 &new_program_id,
370 &owner_id,
371 buffer
372 .get_data()
373 .get(buffer_data_offset..)
374 .ok_or(InstructionError::AccountDataTooSmall)?,
375 clock.slot,
376 invoke_context
377 .get_feature_set()
378 .disable_sbpf_v0_v1_v2_deployment,
379 );
380 drop(buffer);
381
382 let transaction_context = &invoke_context.transaction_context;
383 let instruction_context = transaction_context.get_current_instruction_context()?;
384
385 {
387 let mut programdata = instruction_context.try_borrow_instruction_account(1)?;
388 programdata.set_state(&UpgradeableLoaderState::ProgramData {
389 slot: clock.slot,
390 upgrade_authority_address: authority_key,
391 })?;
392 let dst_slice = programdata
393 .get_data_mut()?
394 .get_mut(
395 programdata_data_offset
396 ..programdata_data_offset.saturating_add(buffer_data_len),
397 )
398 .ok_or(InstructionError::AccountDataTooSmall)?;
399 let mut buffer = instruction_context.try_borrow_instruction_account(3)?;
400 let src_slice = buffer
401 .get_data()
402 .get(buffer_data_offset..)
403 .ok_or(InstructionError::AccountDataTooSmall)?;
404 dst_slice.copy_from_slice(src_slice);
405 buffer.set_data_length(UpgradeableLoaderState::size_of_buffer(0))?;
406 }
407
408 let mut program = instruction_context.try_borrow_instruction_account(2)?;
410 program.set_state(&UpgradeableLoaderState::Program {
411 programdata_address: programdata_key,
412 })?;
413 program.set_executable(true)?;
414 drop(program);
415
416 ic_logger_msg!(log_collector, "Deployed program {:?}", new_program_id);
417 }
418 UpgradeableLoaderInstruction::Upgrade => {
419 instruction_context.check_number_of_instruction_accounts(3)?;
420 let programdata_key = *instruction_context.get_key_of_instruction_account(0)?;
421 let rent =
422 get_sysvar_with_account_check::rent(invoke_context, &instruction_context, 4)?;
423 let clock =
424 get_sysvar_with_account_check::clock(invoke_context, &instruction_context, 5)?;
425 instruction_context.check_number_of_instruction_accounts(7)?;
426 let authority_key = Some(*instruction_context.get_key_of_instruction_account(6)?);
427
428 let set_programdata_to_elf_len = invoke_context
429 .get_feature_set()
430 .loader_v3_set_program_data_to_elf_length;
431
432 let program = instruction_context.try_borrow_instruction_account(1)?;
435 if !program.is_writable() {
436 ic_logger_msg!(log_collector, "Program account not writeable");
437 return Err(InstructionError::InvalidArgument);
438 }
439 if program.get_owner() != program_id {
440 ic_logger_msg!(log_collector, "Program account not owned by loader");
441 return Err(InstructionError::IncorrectProgramId);
442 }
443 if let UpgradeableLoaderState::Program {
444 programdata_address,
445 } = program.get_state()?
446 {
447 if programdata_address != programdata_key {
448 ic_logger_msg!(log_collector, "Program and ProgramData account mismatch");
449 return Err(InstructionError::InvalidArgument);
450 }
451 } else {
452 ic_logger_msg!(log_collector, "Invalid Program account");
453 return Err(InstructionError::InvalidAccountData);
454 }
455 let new_program_id = *program.get_key();
456 drop(program);
457
458 let buffer = instruction_context.try_borrow_instruction_account(2)?;
461 if !buffer.is_writable() {
462 ic_logger_msg!(log_collector, "Buffer account not writeable");
463 return Err(InstructionError::InvalidArgument);
464 }
465 if buffer.get_owner() != program_id {
466 ic_logger_msg!(log_collector, "Buffer account not owned by loader");
467 return Err(InstructionError::IncorrectProgramId);
468 }
469 if let UpgradeableLoaderState::Buffer { authority_address } = buffer.get_state()? {
470 if authority_address != authority_key {
471 ic_logger_msg!(log_collector, "Buffer and upgrade authority don't match");
472 return Err(InstructionError::IncorrectAuthority);
473 }
474 if !instruction_context.is_instruction_account_signer(6)? {
475 ic_logger_msg!(log_collector, "Upgrade authority did not sign");
476 return Err(InstructionError::MissingRequiredSignature);
477 }
478 } else {
479 ic_logger_msg!(log_collector, "Invalid Buffer account");
480 return Err(InstructionError::InvalidArgument);
481 }
482 let buffer_lamports = buffer.get_lamports();
483 let buffer_data_offset = UpgradeableLoaderState::size_of_buffer_metadata();
484 let buffer_data_len = buffer.get_data().len().saturating_sub(buffer_data_offset);
485 if buffer.get_data().len() < UpgradeableLoaderState::size_of_buffer_metadata()
486 || buffer_data_len == 0
487 {
488 ic_logger_msg!(log_collector, "Buffer account too small");
489 return Err(InstructionError::InvalidAccountData);
490 }
491 drop(buffer);
492
493 let programdata = instruction_context.try_borrow_instruction_account(0)?;
496 let programdata_data_offset = UpgradeableLoaderState::size_of_programdata_metadata();
497 let (programdata_len, programdata_balance_required) = if set_programdata_to_elf_len {
498 let new_len = programdata_data_offset.saturating_add(buffer_data_len);
500 if new_len > MAX_PERMITTED_DATA_LENGTH as usize {
501 ic_logger_msg!(
502 log_collector,
503 "Resized ProgramData length of {} bytes exceeds max account data length",
504 new_len
505 );
506 return Err(InstructionError::InvalidAccountData);
507 }
508 (new_len, 1.max(rent.minimum_balance(new_len)))
509 } else {
510 let len = programdata.get_data().len();
513 if len < UpgradeableLoaderState::size_of_programdata(buffer_data_len) {
514 ic_logger_msg!(log_collector, "ProgramData account not large enough");
515 return Err(InstructionError::AccountDataTooSmall);
516 }
517 (len, 1.max(rent.minimum_balance(len)))
518 };
519 if programdata.get_lamports().saturating_add(buffer_lamports)
520 < programdata_balance_required
521 {
522 ic_logger_msg!(
523 log_collector,
524 "Buffer account balance too low to fund upgrade"
525 );
526 return Err(InstructionError::InsufficientFunds);
527 }
528 if let UpgradeableLoaderState::ProgramData {
529 slot,
530 upgrade_authority_address,
531 } = programdata.get_state()?
532 {
533 if clock.slot == slot {
534 ic_logger_msg!(log_collector, "Program was deployed in this block already");
535 return Err(InstructionError::InvalidArgument);
536 }
537 if upgrade_authority_address.is_none() {
538 ic_logger_msg!(log_collector, "Program not upgradeable");
539 return Err(InstructionError::Immutable);
540 }
541 if upgrade_authority_address != authority_key {
542 ic_logger_msg!(log_collector, "Incorrect upgrade authority provided");
543 return Err(InstructionError::IncorrectAuthority);
544 }
545 if !instruction_context.is_instruction_account_signer(6)? {
546 ic_logger_msg!(log_collector, "Upgrade authority did not sign");
547 return Err(InstructionError::MissingRequiredSignature);
548 }
549 } else {
550 ic_logger_msg!(log_collector, "Invalid ProgramData account");
551 return Err(InstructionError::InvalidAccountData);
552 };
553 drop(programdata);
554
555 let buffer = instruction_context.try_borrow_instruction_account(2)?;
557 deploy_program!(
558 invoke_context,
559 &new_program_id,
560 program_id,
561 buffer
562 .get_data()
563 .get(buffer_data_offset..)
564 .ok_or(InstructionError::AccountDataTooSmall)?,
565 clock.slot,
566 invoke_context
567 .get_feature_set()
568 .disable_sbpf_v0_v1_v2_deployment,
569 );
570 drop(buffer);
571
572 let transaction_context = &invoke_context.transaction_context;
573 let instruction_context = transaction_context.get_current_instruction_context()?;
574
575 let mut programdata = instruction_context.try_borrow_instruction_account(0)?;
577 {
578 programdata.set_data_length(programdata_len)?;
579 programdata.set_state(&UpgradeableLoaderState::ProgramData {
580 slot: clock.slot,
581 upgrade_authority_address: authority_key,
582 })?;
583 let dst_slice = programdata
584 .get_data_mut()?
585 .get_mut(
586 programdata_data_offset
587 ..programdata_data_offset.saturating_add(buffer_data_len),
588 )
589 .ok_or(InstructionError::AccountDataTooSmall)?;
590 let buffer = instruction_context.try_borrow_instruction_account(2)?;
591 let src_slice = buffer
592 .get_data()
593 .get(buffer_data_offset..)
594 .ok_or(InstructionError::AccountDataTooSmall)?;
595 dst_slice.copy_from_slice(src_slice);
596 }
597 programdata
598 .get_data_mut()?
599 .get_mut(programdata_data_offset.saturating_add(buffer_data_len)..)
600 .ok_or(InstructionError::AccountDataTooSmall)?
601 .fill(0);
602
603 let mut buffer = instruction_context.try_borrow_instruction_account(2)?;
605 let mut spill = instruction_context.try_borrow_instruction_account(3)?;
606 spill.checked_add_lamports(
607 programdata
608 .get_lamports()
609 .saturating_add(buffer_lamports)
610 .saturating_sub(programdata_balance_required),
611 )?;
612 buffer.set_lamports(0)?;
613 programdata.set_lamports(programdata_balance_required)?;
614 buffer.set_data_length(UpgradeableLoaderState::size_of_buffer(0))?;
615
616 ic_logger_msg!(log_collector, "Upgraded program {:?}", new_program_id);
617 }
618 UpgradeableLoaderInstruction::SetAuthority => {
619 instruction_context.check_number_of_instruction_accounts(2)?;
620 let mut account = instruction_context.try_borrow_instruction_account(0)?;
621 let present_authority_key = instruction_context.get_key_of_instruction_account(1)?;
622 let new_authority = instruction_context.get_key_of_instruction_account(2).ok();
623
624 match account.get_state()? {
625 UpgradeableLoaderState::Buffer { authority_address } => {
626 if new_authority.is_none() {
627 ic_logger_msg!(log_collector, "Buffer authority is not optional");
628 return Err(InstructionError::IncorrectAuthority);
629 }
630 if authority_address.is_none() {
631 ic_logger_msg!(log_collector, "Buffer is immutable");
632 return Err(InstructionError::Immutable);
633 }
634 if authority_address != Some(*present_authority_key) {
635 ic_logger_msg!(log_collector, "Incorrect buffer authority provided");
636 return Err(InstructionError::IncorrectAuthority);
637 }
638 if !instruction_context.is_instruction_account_signer(1)? {
639 ic_logger_msg!(log_collector, "Buffer authority did not sign");
640 return Err(InstructionError::MissingRequiredSignature);
641 }
642 account.set_state(&UpgradeableLoaderState::Buffer {
643 authority_address: new_authority.cloned(),
644 })?;
645 }
646 UpgradeableLoaderState::ProgramData {
647 slot,
648 upgrade_authority_address,
649 } => {
650 if upgrade_authority_address.is_none() {
651 ic_logger_msg!(log_collector, "Program not upgradeable");
652 return Err(InstructionError::Immutable);
653 }
654 if upgrade_authority_address != Some(*present_authority_key) {
655 ic_logger_msg!(log_collector, "Incorrect upgrade authority provided");
656 return Err(InstructionError::IncorrectAuthority);
657 }
658 if !instruction_context.is_instruction_account_signer(1)? {
659 ic_logger_msg!(log_collector, "Upgrade authority did not sign");
660 return Err(InstructionError::MissingRequiredSignature);
661 }
662 if invoke_context
663 .get_feature_set()
664 .disable_sbpf_v0_v1_v2_deployment
665 && new_authority.is_none()
666 && let Some(program) = account
667 .get_data()
668 .get(UpgradeableLoaderState::size_of_programdata_metadata()..)
669 && let Ok(sbpf_version) = get_sbpf_version(program)
670 && sbpf_version < SBPFVersion::V3
671 {
672 ic_logger_msg!(
673 log_collector,
674 "Program targets SBPF {:?}, but finalizing requires SBPF V3 or newer",
675 sbpf_version
676 );
677 return Err(InstructionError::InvalidAccountData);
678 }
679 account.set_state(&UpgradeableLoaderState::ProgramData {
680 slot,
681 upgrade_authority_address: new_authority.cloned(),
682 })?;
683 }
684 _ => {
685 ic_logger_msg!(log_collector, "Account does not support authorities");
686 return Err(InstructionError::InvalidArgument);
687 }
688 }
689
690 ic_logger_msg!(log_collector, "New authority {:?}", new_authority);
691 }
692 UpgradeableLoaderInstruction::SetAuthorityChecked => {
693 if !invoke_context
694 .get_feature_set()
695 .enable_bpf_loader_set_authority_checked_ix
696 {
697 return Err(InstructionError::InvalidInstructionData);
698 }
699
700 instruction_context.check_number_of_instruction_accounts(3)?;
701 let mut account = instruction_context.try_borrow_instruction_account(0)?;
702 let present_authority_key = instruction_context.get_key_of_instruction_account(1)?;
703 let new_authority_key = instruction_context.get_key_of_instruction_account(2)?;
704
705 match account.get_state()? {
706 UpgradeableLoaderState::Buffer { authority_address } => {
707 if authority_address.is_none() {
708 ic_logger_msg!(log_collector, "Buffer is immutable");
709 return Err(InstructionError::Immutable);
710 }
711 if authority_address != Some(*present_authority_key) {
712 ic_logger_msg!(log_collector, "Incorrect buffer authority provided");
713 return Err(InstructionError::IncorrectAuthority);
714 }
715 if !instruction_context.is_instruction_account_signer(1)? {
716 ic_logger_msg!(log_collector, "Buffer authority did not sign");
717 return Err(InstructionError::MissingRequiredSignature);
718 }
719 if !instruction_context.is_instruction_account_signer(2)? {
720 ic_logger_msg!(log_collector, "New authority did not sign");
721 return Err(InstructionError::MissingRequiredSignature);
722 }
723 account.set_state(&UpgradeableLoaderState::Buffer {
724 authority_address: Some(*new_authority_key),
725 })?;
726 }
727 UpgradeableLoaderState::ProgramData {
728 slot,
729 upgrade_authority_address,
730 } => {
731 if upgrade_authority_address.is_none() {
732 ic_logger_msg!(log_collector, "Program not upgradeable");
733 return Err(InstructionError::Immutable);
734 }
735 if upgrade_authority_address != Some(*present_authority_key) {
736 ic_logger_msg!(log_collector, "Incorrect upgrade authority provided");
737 return Err(InstructionError::IncorrectAuthority);
738 }
739 if !instruction_context.is_instruction_account_signer(1)? {
740 ic_logger_msg!(log_collector, "Upgrade authority did not sign");
741 return Err(InstructionError::MissingRequiredSignature);
742 }
743 if !instruction_context.is_instruction_account_signer(2)? {
744 ic_logger_msg!(log_collector, "New authority did not sign");
745 return Err(InstructionError::MissingRequiredSignature);
746 }
747 account.set_state(&UpgradeableLoaderState::ProgramData {
748 slot,
749 upgrade_authority_address: Some(*new_authority_key),
750 })?;
751 }
752 _ => {
753 ic_logger_msg!(log_collector, "Account does not support authorities");
754 return Err(InstructionError::InvalidArgument);
755 }
756 }
757
758 ic_logger_msg!(log_collector, "New authority {:?}", new_authority_key);
759 }
760 UpgradeableLoaderInstruction::Close => {
761 instruction_context.check_number_of_instruction_accounts(2)?;
762 if instruction_context.get_index_of_instruction_account_in_transaction(0)?
763 == instruction_context.get_index_of_instruction_account_in_transaction(1)?
764 {
765 ic_logger_msg!(
766 log_collector,
767 "Recipient is the same as the account being closed"
768 );
769 return Err(InstructionError::InvalidArgument);
770 }
771 let mut close_account = instruction_context.try_borrow_instruction_account(0)?;
772 let close_key = *close_account.get_key();
773 let close_account_state = close_account.get_state()?;
774 match close_account_state {
775 UpgradeableLoaderState::Uninitialized => {
776 let mut recipient_account =
777 instruction_context.try_borrow_instruction_account(1)?;
778 recipient_account.checked_add_lamports(close_account.get_lamports())?;
779 close_account.set_lamports(0)?;
780 close_account
781 .set_data_length(UpgradeableLoaderState::size_of_uninitialized())?;
782 ic_logger_msg!(log_collector, "Closed Uninitialized {}", close_key);
783 }
784 UpgradeableLoaderState::Buffer { authority_address } => {
785 instruction_context.check_number_of_instruction_accounts(3)?;
786 drop(close_account);
787 common_close_account(&authority_address, &instruction_context, &log_collector)?;
788 ic_logger_msg!(log_collector, "Closed Buffer {}", close_key);
789 }
790 UpgradeableLoaderState::ProgramData {
791 slot,
792 upgrade_authority_address: authority_address,
793 } => {
794 instruction_context.check_number_of_instruction_accounts(4)?;
795 drop(close_account);
796 let program_account = instruction_context.try_borrow_instruction_account(3)?;
797 let program_key = *program_account.get_key();
798
799 if !program_account.is_writable() {
800 ic_logger_msg!(log_collector, "Program account is not writable");
801 return Err(InstructionError::InvalidArgument);
802 }
803 if program_account.get_owner() != program_id {
804 ic_logger_msg!(log_collector, "Program account not owned by loader");
805 return Err(InstructionError::IncorrectProgramId);
806 }
807 let clock = invoke_context
808 .environment_config
809 .sysvar_cache()
810 .get_clock()?;
811 if clock.slot == slot {
812 ic_logger_msg!(log_collector, "Program was deployed in this block already");
813 return Err(InstructionError::InvalidArgument);
814 }
815
816 match program_account.get_state()? {
817 UpgradeableLoaderState::Program {
818 programdata_address,
819 } => {
820 if programdata_address != close_key {
821 ic_logger_msg!(
822 log_collector,
823 "ProgramData account does not match ProgramData account"
824 );
825 return Err(InstructionError::InvalidArgument);
826 }
827
828 drop(program_account);
829 common_close_account(
830 &authority_address,
831 &instruction_context,
832 &log_collector,
833 )?;
834 let clock = invoke_context
835 .environment_config
836 .sysvar_cache()
837 .get_clock()?;
838 invoke_context
839 .program_cache_for_tx_batch
840 .store_modified_entry(
841 program_key,
842 Arc::new(ProgramCacheEntry::new_closed_tombstone(
843 clock.slot,
844 ProgramCacheEntryOwner::LoaderV3,
845 )),
846 );
847 }
848 _ => {
849 ic_logger_msg!(log_collector, "Invalid Program account");
850 return Err(InstructionError::InvalidArgument);
851 }
852 }
853
854 ic_logger_msg!(log_collector, "Closed Program {}", program_key);
855 }
856 _ => {
857 ic_logger_msg!(log_collector, "Account does not support closing");
858 return Err(InstructionError::InvalidArgument);
859 }
860 }
861 }
862 UpgradeableLoaderInstruction::ExtendProgram { additional_bytes } => {
863 common_extend_program(invoke_context, additional_bytes, false)?;
864 }
865 }
866
867 Ok(())
868}
869
870fn common_extend_program(
871 invoke_context: &mut InvokeContext,
872 additional_bytes: u32,
873 check_authority: bool,
874) -> Result<(), InstructionError> {
875 let log_collector = invoke_context.get_log_collector();
876 let transaction_context = &invoke_context.transaction_context;
877 let instruction_context = transaction_context.get_current_instruction_context()?;
878 let program_id = instruction_context.get_program_key()?;
879
880 const PROGRAM_DATA_ACCOUNT_INDEX: IndexOfAccount = 0;
881 const PROGRAM_ACCOUNT_INDEX: IndexOfAccount = 1;
882 const AUTHORITY_ACCOUNT_INDEX: IndexOfAccount = 2;
883 let optional_payer_account_index = if check_authority { 4 } else { 3 };
885
886 if additional_bytes == 0 {
887 ic_logger_msg!(log_collector, "Additional bytes must be greater than 0");
888 return Err(InstructionError::InvalidInstructionData);
889 }
890
891 let programdata_account =
892 instruction_context.try_borrow_instruction_account(PROGRAM_DATA_ACCOUNT_INDEX)?;
893 let programdata_key = *programdata_account.get_key();
894
895 if program_id != programdata_account.get_owner() {
896 ic_logger_msg!(log_collector, "ProgramData owner is invalid");
897 return Err(InstructionError::InvalidAccountOwner);
898 }
899 if !programdata_account.is_writable() {
900 ic_logger_msg!(log_collector, "ProgramData is not writable");
901 return Err(InstructionError::InvalidArgument);
902 }
903
904 let program_account =
905 instruction_context.try_borrow_instruction_account(PROGRAM_ACCOUNT_INDEX)?;
906 if !program_account.is_writable() {
907 ic_logger_msg!(log_collector, "Program account is not writable");
908 return Err(InstructionError::InvalidArgument);
909 }
910 if program_account.get_owner() != program_id {
911 ic_logger_msg!(log_collector, "Program account not owned by loader");
912 return Err(InstructionError::InvalidAccountOwner);
913 }
914 let program_key = *program_account.get_key();
915 match program_account.get_state()? {
916 UpgradeableLoaderState::Program {
917 programdata_address,
918 } => {
919 if programdata_address != programdata_key {
920 ic_logger_msg!(
921 log_collector,
922 "Program account does not match ProgramData account"
923 );
924 return Err(InstructionError::InvalidArgument);
925 }
926 }
927 _ => {
928 ic_logger_msg!(log_collector, "Invalid Program account");
929 return Err(InstructionError::InvalidAccountData);
930 }
931 }
932 drop(program_account);
933
934 let old_len = programdata_account.get_data().len();
935 let new_len = old_len.saturating_add(additional_bytes as usize);
936 if new_len > MAX_PERMITTED_DATA_LENGTH as usize {
937 ic_logger_msg!(
938 log_collector,
939 "Extended ProgramData length of {} bytes exceeds max account data length of {} bytes",
940 new_len,
941 MAX_PERMITTED_DATA_LENGTH
942 );
943 return Err(InstructionError::InvalidRealloc);
944 }
945
946 let headroom = (MAX_PERMITTED_DATA_LENGTH as usize).saturating_sub(old_len);
952 if additional_bytes < MINIMUM_EXTEND_PROGRAM_BYTES && (additional_bytes as usize) != headroom {
953 ic_logger_msg!(
954 log_collector,
955 "ExtendProgram requires a minimum of {} additional bytes or to extend to maximum \
956 size, but only {} were requested",
957 MINIMUM_EXTEND_PROGRAM_BYTES,
958 additional_bytes,
959 );
960 return Err(InstructionError::InvalidArgument);
961 }
962
963 let clock_slot = invoke_context
964 .environment_config
965 .sysvar_cache()
966 .get_clock()
967 .map(|clock| clock.slot)?;
968
969 let upgrade_authority_address = if let UpgradeableLoaderState::ProgramData {
970 slot,
971 upgrade_authority_address,
972 } = programdata_account.get_state()?
973 {
974 if clock_slot == slot {
975 ic_logger_msg!(log_collector, "Program was extended in this block already");
976 return Err(InstructionError::InvalidArgument);
977 }
978
979 if upgrade_authority_address.is_none() {
980 ic_logger_msg!(
981 log_collector,
982 "Cannot extend ProgramData accounts that are not upgradeable"
983 );
984 return Err(InstructionError::Immutable);
985 }
986
987 if check_authority {
988 let authority_key =
989 Some(*instruction_context.get_key_of_instruction_account(AUTHORITY_ACCOUNT_INDEX)?);
990 if upgrade_authority_address != authority_key {
991 ic_logger_msg!(log_collector, "Incorrect upgrade authority provided");
992 return Err(InstructionError::IncorrectAuthority);
993 }
994 if !instruction_context.is_instruction_account_signer(AUTHORITY_ACCOUNT_INDEX)? {
995 ic_logger_msg!(log_collector, "Upgrade authority did not sign");
996 return Err(InstructionError::MissingRequiredSignature);
997 }
998 }
999
1000 upgrade_authority_address
1001 } else {
1002 ic_logger_msg!(log_collector, "ProgramData state is invalid");
1003 return Err(InstructionError::InvalidAccountData);
1004 };
1005
1006 let required_payment = {
1007 let balance = programdata_account.get_lamports();
1008 let rent = invoke_context
1009 .environment_config
1010 .sysvar_cache()
1011 .get_rent()?;
1012 let min_balance = rent.minimum_balance(new_len).max(1);
1013 min_balance.saturating_sub(balance)
1014 };
1015
1016 drop(programdata_account);
1018
1019 let program_id = *program_id;
1021 if required_payment > 0 {
1022 let payer_key =
1023 *instruction_context.get_key_of_instruction_account(optional_payer_account_index)?;
1024
1025 invoke_context.native_invoke_signed(
1026 system_instruction::transfer(&payer_key, &programdata_key, required_payment),
1027 &[],
1028 )?;
1029 }
1030
1031 let transaction_context = &invoke_context.transaction_context;
1032 let instruction_context = transaction_context.get_current_instruction_context()?;
1033 let mut programdata_account =
1034 instruction_context.try_borrow_instruction_account(PROGRAM_DATA_ACCOUNT_INDEX)?;
1035 programdata_account.set_data_length(new_len)?;
1036
1037 let programdata_data_offset = UpgradeableLoaderState::size_of_programdata_metadata();
1038
1039 deploy_program!(
1040 invoke_context,
1041 &program_key,
1042 &program_id,
1043 programdata_account
1044 .get_data()
1045 .get(programdata_data_offset..)
1046 .ok_or(InstructionError::AccountDataTooSmall)?,
1047 clock_slot,
1048 false, );
1050 drop(programdata_account);
1051
1052 let mut programdata_account =
1053 instruction_context.try_borrow_instruction_account(PROGRAM_DATA_ACCOUNT_INDEX)?;
1054 programdata_account.set_state(&UpgradeableLoaderState::ProgramData {
1055 slot: clock_slot,
1056 upgrade_authority_address,
1057 })?;
1058
1059 ic_logger_msg!(
1060 log_collector,
1061 "Extended ProgramData account by {} bytes",
1062 additional_bytes
1063 );
1064
1065 Ok(())
1066}
1067
1068fn common_close_account(
1069 authority_address: &Option<Pubkey>,
1070 instruction_context: &InstructionContext,
1071 log_collector: &Option<Rc<RefCell<LogCollector>>>,
1072) -> Result<(), InstructionError> {
1073 if authority_address.is_none() {
1074 ic_logger_msg!(log_collector, "Account is immutable");
1075 return Err(InstructionError::Immutable);
1076 }
1077 if *authority_address != Some(*instruction_context.get_key_of_instruction_account(2)?) {
1078 ic_logger_msg!(log_collector, "Incorrect authority provided");
1079 return Err(InstructionError::IncorrectAuthority);
1080 }
1081 if !instruction_context.is_instruction_account_signer(2)? {
1082 ic_logger_msg!(log_collector, "Authority did not sign");
1083 return Err(InstructionError::MissingRequiredSignature);
1084 }
1085
1086 let mut close_account = instruction_context.try_borrow_instruction_account(0)?;
1087 let mut recipient_account = instruction_context.try_borrow_instruction_account(1)?;
1088
1089 recipient_account.checked_add_lamports(close_account.get_lamports())?;
1090 close_account.set_lamports(0)?;
1091 close_account.set_data_length(UpgradeableLoaderState::size_of_uninitialized())?;
1092 close_account.set_state(&UpgradeableLoaderState::Uninitialized)?;
1093 Ok(())
1094}
1095
1096#[cfg_attr(feature = "svm-internal", qualifiers(pub))]
1097mod test_utils {
1098 #[cfg(all(feature = "svm-internal", feature = "metrics"))]
1099 use solana_program_runtime::program_metrics::LoadProgramMetrics;
1100 #[cfg(feature = "svm-internal")]
1101 use {
1102 super::*, solana_account::ReadableAccount,
1103 solana_program_runtime::loaded_programs::ProgramRuntimeEnvironment,
1104 solana_syscalls::create_program_runtime_environment,
1105 };
1106
1107 #[cfg(feature = "svm-internal")]
1108 fn check_loader_id(id: &Pubkey) -> bool {
1109 bpf_loader::check_id(id)
1110 || bpf_loader_deprecated::check_id(id)
1111 || bpf_loader_upgradeable::check_id(id)
1112 }
1113
1114 #[cfg(feature = "svm-internal")]
1115 #[cfg_attr(feature = "svm-internal", qualifiers(pub))]
1116 fn load_all_invoked_programs(invoke_context: &mut InvokeContext) {
1117 let program_runtime_environment = create_program_runtime_environment(
1118 invoke_context.get_feature_set(),
1119 invoke_context.get_compute_budget(),
1120 false, false, )
1123 .unwrap();
1124 let num_accounts = invoke_context.transaction_context.get_number_of_accounts();
1125 for index in 0..num_accounts {
1126 let account = invoke_context
1127 .transaction_context
1128 .accounts()
1129 .try_borrow(index)
1130 .expect("Failed to get the account");
1131
1132 let owner = account.owner();
1133 if check_loader_id(owner) {
1134 let programdata_data_offset = 0;
1135 let pubkey = invoke_context
1136 .transaction_context
1137 .get_key_of_account_at_index(index)
1138 .expect("Failed to get account key");
1139
1140 let programdata = account
1141 .data()
1142 .get(programdata_data_offset.min(account.data().len())..)
1143 .unwrap();
1144 let loaded_program = ProgramCacheEntry::load(
1145 owner,
1146 ProgramRuntimeEnvironment::clone(&program_runtime_environment),
1147 0,
1148 programdata,
1149 #[cfg(feature = "metrics")]
1150 &mut LoadProgramMetrics::default(),
1151 )
1152 .map_err(|_| InstructionError::InvalidAccountData);
1153 if let Ok(loaded_program) = loaded_program {
1154 invoke_context
1155 .program_cache_for_tx_batch
1156 .store_modified_entry(*pubkey, Arc::new(loaded_program));
1157 }
1158 }
1159 }
1160 }
1161}
1162
1163#[cfg(test)]
1164mod tests {
1165 use {
1166 super::*,
1167 assert_matches::assert_matches,
1168 solana_account::{AccountSharedData, state_traits::StateMutWincode as _},
1169 solana_epoch_schedule::EpochSchedule,
1170 solana_instruction_error::InstructionError,
1171 solana_program_runtime::{
1172 loaded_programs::ProgramRuntimeEnvironment, program_metrics::ProgramStatistics,
1173 vm::calculate_heap_cost, with_mock_invoke_context,
1174 },
1175 solana_pubkey::Pubkey,
1176 solana_sbpf::program::BuiltinProgram,
1177 solana_sdk_ids::sysvar,
1178 solana_svm_type_overrides::sync::atomic::{AtomicU64, Ordering},
1179 solana_sysvar_id::SysvarId,
1180 std::{fs::File, io::Read},
1181 test_case::test_case,
1182 };
1183
1184 fn create_sysvar_account<T>(value: &T) -> AccountSharedData
1185 where
1186 T: wincode::SchemaWrite<solana_account::WincodeConfig, Src = T>
1187 + for<'de> wincode::SchemaRead<'de, solana_account::WincodeConfig, Dst = T>
1188 + SysvarId,
1189 {
1190 let serialized_len = wincode::config::serialized_size(value, solana_account::WINCODE_CONFIG)
1191 .unwrap() as usize;
1192 let canonical_data_len = match T::id() {
1193 sysvar::clock::ID => solana_clock::SIZE,
1194 sysvar::epoch_schedule::ID => solana_epoch_schedule::SIZE,
1195 sysvar::rent::ID => solana_rent::SIZE,
1196 id => panic!("unsupported sysvar: {id}"),
1197 };
1198 let required_data_len = canonical_data_len.max(serialized_len);
1199 AccountSharedData::new_data_with_space(1, value, required_data_len, &sysvar::id()).unwrap()
1200 }
1201
1202 #[test_case(false; "legacy")]
1203 #[test_case(true; "SIMD-0648")]
1204 fn test_write_instruction_matches_sdk(unbound_loader_v3_instruction_data: bool) {
1205 let instruction = UpgradeableLoaderInstruction::Write {
1206 offset: 7,
1207 bytes: vec![1, 2, 3],
1208 };
1209 let UpgradeableLoaderInstruction::Write { offset, bytes } = &instruction else {
1212 unreachable!()
1213 };
1214 let serialized = bincode::serialize(&instruction).unwrap();
1215 assert!(serialized.starts_with(&WRITE_INSTRUCTION_TAG));
1216 assert_eq!(
1217 parse_write_instruction(&serialized, unbound_loader_v3_instruction_data).unwrap(),
1218 (*offset, bytes.as_slice())
1219 );
1220 }
1221
1222 #[test]
1223 fn test_calculate_heap_cost() {
1224 let heap_cost = 8_u64;
1225
1226 assert_eq!(0, calculate_heap_cost(31 * 1024, heap_cost));
1230
1231 assert_eq!(0, calculate_heap_cost(32 * 1024, heap_cost));
1233
1234 assert_eq!(heap_cost, calculate_heap_cost(33 * 1024, heap_cost));
1236
1237 assert_eq!(heap_cost, calculate_heap_cost(64 * 1024, heap_cost));
1239 }
1240
1241 fn deploy_test_program(
1242 invoke_context: &mut InvokeContext,
1243 program_id: Pubkey,
1244 ) -> Result<(), InstructionError> {
1245 let mut file = File::open("test_elfs/out/sbpfv3_return_ok.so").expect("file open failed");
1246 let mut elf = Vec::new();
1247 file.read_to_end(&mut elf).unwrap();
1248 deploy_program!(
1249 invoke_context,
1250 &program_id,
1251 &bpf_loader_upgradeable::id(),
1252 &elf,
1253 2_u64,
1254 true, );
1256 Ok(())
1257 }
1258
1259 #[cfg(feature = "shuttle-test")]
1278 const PROGRAM_USAGE_COUNT_RANDOM_ITERATIONS: usize = 100;
1279
1280 #[test]
1281 fn test_program_usage_count_on_upgrade() {
1282 #[cfg(feature = "shuttle-test")]
1283 shuttle::check_random(
1284 do_test_program_usage_count_on_upgrade,
1285 PROGRAM_USAGE_COUNT_RANDOM_ITERATIONS,
1286 );
1287 #[cfg(not(feature = "shuttle-test"))]
1288 do_test_program_usage_count_on_upgrade();
1289 }
1290
1291 fn do_test_program_usage_count_on_upgrade() {
1292 let transaction_accounts = vec![(
1293 sysvar::epoch_schedule::id(),
1294 create_sysvar_account(&EpochSchedule::default()),
1295 )];
1296 with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
1297 let program_id = Pubkey::new_unique();
1298 let env = ProgramRuntimeEnvironment::from(BuiltinProgram::new_mock());
1299 let stats = ProgramStatistics {
1300 uses: 100.into(),
1301 ..Default::default()
1302 };
1303 let program = ProgramCacheEntry {
1304 program: ProgramCacheEntryType::Unloaded(env),
1305 account_owner: ProgramCacheEntryOwner::LoaderV2,
1306 deployment_slot: 0,
1307 stats: stats.into(),
1308 latest_access_slot: AtomicU64::new(0),
1309 };
1310 invoke_context
1311 .program_cache_for_tx_batch
1312 .replenish(program_id, Arc::new(program));
1313 invoke_context
1314 .program_cache_for_tx_batch
1315 .set_slot_for_tests(2);
1316
1317 assert_matches!(
1318 deploy_test_program(&mut invoke_context, program_id,),
1319 Ok(())
1320 );
1321
1322 let updated_program = invoke_context
1323 .program_cache_for_tx_batch
1324 .find(&program_id)
1325 .expect("Didn't find upgraded program in the cache");
1326
1327 assert_eq!(updated_program.deployment_slot, 2);
1328 assert_eq!(updated_program.stats.uses.load(Ordering::Relaxed), 100);
1329 }
1330
1331 #[test]
1332 fn test_program_usage_count_on_non_upgrade() {
1333 #[cfg(feature = "shuttle-test")]
1334 shuttle::check_random(
1335 do_test_program_usage_count_on_non_upgrade,
1336 PROGRAM_USAGE_COUNT_RANDOM_ITERATIONS,
1337 );
1338 #[cfg(not(feature = "shuttle-test"))]
1339 do_test_program_usage_count_on_non_upgrade();
1340 }
1341
1342 fn do_test_program_usage_count_on_non_upgrade() {
1343 let transaction_accounts = vec![(
1344 sysvar::epoch_schedule::id(),
1345 create_sysvar_account(&EpochSchedule::default()),
1346 )];
1347 with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
1348 let program_id = Pubkey::new_unique();
1349 let env = ProgramRuntimeEnvironment::from(BuiltinProgram::new_mock());
1350 let stats = ProgramStatistics {
1351 uses: 100.into(),
1352 ..Default::default()
1353 };
1354 let program = ProgramCacheEntry {
1355 program: ProgramCacheEntryType::Unloaded(env),
1356 account_owner: ProgramCacheEntryOwner::LoaderV2,
1357 deployment_slot: 0,
1358 stats: stats.into(),
1359 latest_access_slot: AtomicU64::new(0),
1360 };
1361 invoke_context
1362 .program_cache_for_tx_batch
1363 .replenish(program_id, Arc::new(program));
1364 invoke_context
1365 .program_cache_for_tx_batch
1366 .set_slot_for_tests(2);
1367
1368 let program_id2 = Pubkey::new_unique();
1369 assert_matches!(
1370 deploy_test_program(&mut invoke_context, program_id2),
1371 Ok(())
1372 );
1373
1374 let program2 = invoke_context
1375 .program_cache_for_tx_batch
1376 .find(&program_id2)
1377 .expect("Didn't find upgraded program in the cache");
1378
1379 assert_eq!(program2.deployment_slot, 2);
1380 assert_eq!(program2.stats.uses.load(Ordering::Relaxed), 0);
1381 }
1382}