1#[cfg(feature = "bincode")]
4use {
5 crate::{get_program_data_address, state::UpgradeableLoaderState},
6 solana_instruction::{error::InstructionError, AccountMeta, Instruction},
7 solana_pubkey::Pubkey,
8 solana_sdk_ids::{bpf_loader_upgradeable::id, sysvar},
9 solana_system_interface::instruction as system_instruction,
10};
11
12pub const MINIMUM_EXTEND_PROGRAM_BYTES: u32 = 10_240;
19
20#[repr(u8)]
21#[cfg_attr(
22 feature = "serde",
23 derive(serde_derive::Deserialize, serde_derive::Serialize)
24)]
25#[derive(Debug, PartialEq, Eq, Clone)]
26pub enum UpgradeableLoaderInstruction {
27 InitializeBuffer,
43
44 Write {
50 offset: u32,
52 #[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]
54 bytes: Vec<u8>,
55 },
56
57 DeployWithMaxDataLen {
99 max_data_len: usize,
101 },
102
103 Upgrade,
124
125 SetAuthority,
136
137 Close,
149
150 ExtendProgram {
172 additional_bytes: u32,
174 },
175
176 SetAuthorityChecked,
188}
189
190#[cfg(feature = "bincode")]
191pub fn create_buffer(
193 payer_address: &Pubkey,
194 buffer_address: &Pubkey,
195 authority_address: &Pubkey,
196 lamports: u64,
197 program_len: usize,
198) -> Result<Vec<Instruction>, InstructionError> {
199 Ok(vec![
200 system_instruction::create_account(
201 payer_address,
202 buffer_address,
203 lamports,
204 UpgradeableLoaderState::size_of_buffer(program_len) as u64,
205 &id(),
206 ),
207 Instruction::new_with_bincode(
208 id(),
209 &UpgradeableLoaderInstruction::InitializeBuffer,
210 vec![
211 AccountMeta::new(*buffer_address, false),
212 AccountMeta::new_readonly(*authority_address, false),
213 ],
214 ),
215 ])
216}
217
218#[cfg(feature = "bincode")]
219pub fn write(
222 buffer_address: &Pubkey,
223 authority_address: &Pubkey,
224 offset: u32,
225 bytes: Vec<u8>,
226) -> Instruction {
227 Instruction::new_with_bincode(
228 id(),
229 &UpgradeableLoaderInstruction::Write { offset, bytes },
230 vec![
231 AccountMeta::new(*buffer_address, false),
232 AccountMeta::new_readonly(*authority_address, true),
233 ],
234 )
235}
236
237#[cfg(feature = "bincode")]
238pub fn deploy_with_max_program_len(
242 payer_address: &Pubkey,
243 program_address: &Pubkey,
244 buffer_address: &Pubkey,
245 upgrade_authority_address: &Pubkey,
246 program_lamports: u64,
247 max_data_len: usize,
248) -> Result<Vec<Instruction>, InstructionError> {
249 let programdata_address = get_program_data_address(program_address);
250 Ok(vec![
251 system_instruction::create_account(
252 payer_address,
253 program_address,
254 program_lamports,
255 UpgradeableLoaderState::size_of_program() as u64,
256 &id(),
257 ),
258 Instruction::new_with_bincode(
259 id(),
260 &UpgradeableLoaderInstruction::DeployWithMaxDataLen { max_data_len },
261 vec![
262 AccountMeta::new(*payer_address, true),
263 AccountMeta::new(programdata_address, false),
264 AccountMeta::new(*program_address, false),
265 AccountMeta::new(*buffer_address, false),
266 AccountMeta::new_readonly(sysvar::rent::id(), false),
267 AccountMeta::new_readonly(sysvar::clock::id(), false),
268 AccountMeta::new_readonly(solana_sdk_ids::system_program::id(), false),
269 AccountMeta::new_readonly(*upgrade_authority_address, true),
270 ],
271 ),
272 ])
273}
274
275#[cfg(feature = "bincode")]
276pub fn upgrade(
278 program_address: &Pubkey,
279 buffer_address: &Pubkey,
280 authority_address: &Pubkey,
281 spill_address: &Pubkey,
282) -> Instruction {
283 let programdata_address = get_program_data_address(program_address);
284 Instruction::new_with_bincode(
285 id(),
286 &UpgradeableLoaderInstruction::Upgrade,
287 vec![
288 AccountMeta::new(programdata_address, false),
289 AccountMeta::new(*program_address, false),
290 AccountMeta::new(*buffer_address, false),
291 AccountMeta::new(*spill_address, false),
292 AccountMeta::new_readonly(sysvar::rent::id(), false),
293 AccountMeta::new_readonly(sysvar::clock::id(), false),
294 AccountMeta::new_readonly(*authority_address, true),
295 ],
296 )
297}
298
299pub fn is_upgrade_instruction(instruction_data: &[u8]) -> bool {
300 !instruction_data.is_empty() && 3 == instruction_data[0]
301}
302
303pub fn is_set_authority_instruction(instruction_data: &[u8]) -> bool {
304 !instruction_data.is_empty() && 4 == instruction_data[0]
305}
306
307pub fn is_close_instruction(instruction_data: &[u8]) -> bool {
308 !instruction_data.is_empty() && 5 == instruction_data[0]
309}
310
311pub fn is_set_authority_checked_instruction(instruction_data: &[u8]) -> bool {
312 !instruction_data.is_empty() && 7 == instruction_data[0]
313}
314
315#[cfg(feature = "bincode")]
316pub fn set_buffer_authority(
318 buffer_address: &Pubkey,
319 current_authority_address: &Pubkey,
320 new_authority_address: &Pubkey,
321) -> Instruction {
322 Instruction::new_with_bincode(
323 id(),
324 &UpgradeableLoaderInstruction::SetAuthority,
325 vec![
326 AccountMeta::new(*buffer_address, false),
327 AccountMeta::new_readonly(*current_authority_address, true),
328 AccountMeta::new_readonly(*new_authority_address, false),
329 ],
330 )
331}
332
333#[cfg(feature = "bincode")]
334pub fn set_buffer_authority_checked(
337 buffer_address: &Pubkey,
338 current_authority_address: &Pubkey,
339 new_authority_address: &Pubkey,
340) -> Instruction {
341 Instruction::new_with_bincode(
342 id(),
343 &UpgradeableLoaderInstruction::SetAuthorityChecked,
344 vec![
345 AccountMeta::new(*buffer_address, false),
346 AccountMeta::new_readonly(*current_authority_address, true),
347 AccountMeta::new_readonly(*new_authority_address, true),
348 ],
349 )
350}
351
352#[cfg(feature = "bincode")]
353pub fn set_upgrade_authority(
355 program_address: &Pubkey,
356 current_authority_address: &Pubkey,
357 new_authority_address: Option<&Pubkey>,
358) -> Instruction {
359 let programdata_address = get_program_data_address(program_address);
360
361 let mut metas = vec![
362 AccountMeta::new(programdata_address, false),
363 AccountMeta::new_readonly(*current_authority_address, true),
364 ];
365 if let Some(address) = new_authority_address {
366 metas.push(AccountMeta::new_readonly(*address, false));
367 }
368 Instruction::new_with_bincode(id(), &UpgradeableLoaderInstruction::SetAuthority, metas)
369}
370
371#[cfg(feature = "bincode")]
372pub fn set_upgrade_authority_checked(
375 program_address: &Pubkey,
376 current_authority_address: &Pubkey,
377 new_authority_address: &Pubkey,
378) -> Instruction {
379 let programdata_address = get_program_data_address(program_address);
380
381 let metas = vec![
382 AccountMeta::new(programdata_address, false),
383 AccountMeta::new_readonly(*current_authority_address, true),
384 AccountMeta::new_readonly(*new_authority_address, true),
385 ];
386 Instruction::new_with_bincode(
387 id(),
388 &UpgradeableLoaderInstruction::SetAuthorityChecked,
389 metas,
390 )
391}
392
393#[cfg(feature = "bincode")]
394pub fn close(
396 close_address: &Pubkey,
397 recipient_address: &Pubkey,
398 authority_address: &Pubkey,
399) -> Instruction {
400 close_any(
401 close_address,
402 recipient_address,
403 Some(authority_address),
404 None,
405 )
406}
407
408#[cfg(feature = "bincode")]
409pub fn close_any(
411 close_address: &Pubkey,
412 recipient_address: &Pubkey,
413 authority_address: Option<&Pubkey>,
414 program_address: Option<&Pubkey>,
415) -> Instruction {
416 let mut metas = vec![
417 AccountMeta::new(*close_address, false),
418 AccountMeta::new(*recipient_address, false),
419 ];
420 if let Some(authority_address) = authority_address {
421 metas.push(AccountMeta::new_readonly(*authority_address, true));
422 }
423 if let Some(program_address) = program_address {
424 metas.push(AccountMeta::new(*program_address, false));
425 }
426 Instruction::new_with_bincode(id(), &UpgradeableLoaderInstruction::Close, metas)
427}
428
429#[cfg(feature = "bincode")]
430pub fn extend_program(
437 program_address: &Pubkey,
438 payer_address: Option<&Pubkey>,
439 additional_bytes: u32,
440) -> Instruction {
441 let program_data_address = get_program_data_address(program_address);
442 let mut metas = vec![
443 AccountMeta::new(program_data_address, false),
444 AccountMeta::new(*program_address, false),
445 ];
446 if let Some(payer_address) = payer_address {
447 metas.push(AccountMeta::new_readonly(
448 solana_sdk_ids::system_program::id(),
449 false,
450 ));
451 metas.push(AccountMeta::new(*payer_address, true));
452 }
453 Instruction::new_with_bincode(
454 id(),
455 &UpgradeableLoaderInstruction::ExtendProgram { additional_bytes },
456 metas,
457 )
458}
459
460#[cfg(test)]
461mod tests {
462 use super::*;
463
464 fn assert_is_instruction<F>(
465 is_instruction_fn: F,
466 expected_instruction: UpgradeableLoaderInstruction,
467 ) where
468 F: Fn(&[u8]) -> bool,
469 {
470 let result = is_instruction_fn(
471 &bincode::serialize(&UpgradeableLoaderInstruction::InitializeBuffer).unwrap(),
472 );
473 let expected_result = matches!(
474 expected_instruction,
475 UpgradeableLoaderInstruction::InitializeBuffer
476 );
477 assert_eq!(expected_result, result);
478
479 let result = is_instruction_fn(
480 &bincode::serialize(&UpgradeableLoaderInstruction::Write {
481 offset: 0,
482 bytes: vec![],
483 })
484 .unwrap(),
485 );
486 let expected_result = matches!(
487 expected_instruction,
488 UpgradeableLoaderInstruction::Write {
489 offset: _,
490 bytes: _,
491 }
492 );
493 assert_eq!(expected_result, result);
494
495 let result = is_instruction_fn(
496 &bincode::serialize(&UpgradeableLoaderInstruction::DeployWithMaxDataLen {
497 max_data_len: 0,
498 })
499 .unwrap(),
500 );
501 let expected_result = matches!(
502 expected_instruction,
503 UpgradeableLoaderInstruction::DeployWithMaxDataLen { max_data_len: _ }
504 );
505 assert_eq!(expected_result, result);
506
507 let result =
508 is_instruction_fn(&bincode::serialize(&UpgradeableLoaderInstruction::Upgrade).unwrap());
509 let expected_result = matches!(expected_instruction, UpgradeableLoaderInstruction::Upgrade);
510 assert_eq!(expected_result, result);
511
512 let result = is_instruction_fn(
513 &bincode::serialize(&UpgradeableLoaderInstruction::SetAuthority).unwrap(),
514 );
515 let expected_result = matches!(
516 expected_instruction,
517 UpgradeableLoaderInstruction::SetAuthority
518 );
519 assert_eq!(expected_result, result);
520
521 let result =
522 is_instruction_fn(&bincode::serialize(&UpgradeableLoaderInstruction::Close).unwrap());
523 let expected_result = matches!(expected_instruction, UpgradeableLoaderInstruction::Close);
524 assert_eq!(expected_result, result);
525 }
526
527 #[test]
528 fn test_is_set_authority_instruction() {
529 assert!(!is_set_authority_instruction(&[]));
530 assert_is_instruction(
531 is_set_authority_instruction,
532 UpgradeableLoaderInstruction::SetAuthority {},
533 );
534 }
535
536 #[test]
537 fn test_is_set_authority_checked_instruction() {
538 assert!(!is_set_authority_checked_instruction(&[]));
539 assert_is_instruction(
540 is_set_authority_checked_instruction,
541 UpgradeableLoaderInstruction::SetAuthorityChecked {},
542 );
543 }
544
545 #[test]
546 fn test_is_upgrade_instruction() {
547 assert!(!is_upgrade_instruction(&[]));
548 assert_is_instruction(
549 is_upgrade_instruction,
550 UpgradeableLoaderInstruction::Upgrade {},
551 );
552 }
553}