1#[cfg(feature = "wincode")]
4use {
5 crate::{get_program_data_address, state::UpgradeableLoaderState},
6 core::mem::MaybeUninit,
7 solana_instruction::{error::InstructionError, AccountMeta, Instruction},
8 solana_pubkey::Pubkey,
9 solana_sdk_ids::{bpf_loader_upgradeable::id, sysvar},
10 solana_system_interface::instruction as system_instruction,
11 wincode::{
12 config::ConfigCore,
13 io::{Reader, Writer},
14 ReadResult, SchemaRead, SchemaWrite, TypeMeta, WriteResult,
15 },
16};
17
18pub const MINIMUM_EXTEND_PROGRAM_BYTES: u32 = 10_240;
25
26#[repr(u8)]
27#[cfg_attr(
28 feature = "serde",
29 derive(serde_derive::Deserialize, serde_derive::Serialize)
30)]
31#[cfg_attr(feature = "wincode", derive(SchemaRead, SchemaWrite))]
32#[derive(Debug, PartialEq, Eq, Clone)]
33pub enum UpgradeableLoaderInstruction {
34 InitializeBuffer,
50
51 Write {
57 offset: u32,
59 #[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]
61 bytes: Vec<u8>,
62 },
63
64 DeployWithMaxDataLen {
106 max_data_len: usize,
108 #[cfg_attr(feature = "wincode", wincode(with = "OptionalTrailingBool<true>"))]
113 close_buffer: bool,
114 },
115
116 Upgrade {
137 #[cfg_attr(feature = "wincode", wincode(with = "OptionalTrailingBool<true>"))]
142 close_buffer: bool,
143 },
144
145 SetAuthority,
156
157 Close {
169 #[cfg_attr(feature = "wincode", wincode(with = "OptionalTrailingBool<false>"))]
175 tombstone: bool,
176 },
177
178 ExtendProgram {
200 additional_bytes: u32,
202 },
203
204 SetAuthorityChecked,
216}
217
218#[cfg(feature = "wincode")]
222pub struct OptionalTrailingBool<const DEFAULT: bool>;
223
224#[cfg(feature = "wincode")]
225unsafe impl<'de, C: ConfigCore, const DEFAULT: bool> SchemaRead<'de, C>
226 for OptionalTrailingBool<DEFAULT>
227{
228 type Dst = bool;
229
230 fn read(mut reader: impl Reader<'de>, dst: &mut MaybeUninit<Self::Dst>) -> ReadResult<()> {
231 let value = reader.take_byte().map(|b| b != 0).unwrap_or(DEFAULT);
232 dst.write(value);
233 Ok(())
234 }
235}
236
237#[cfg(feature = "wincode")]
238unsafe impl<C: ConfigCore, const DEFAULT: bool> SchemaWrite<C> for OptionalTrailingBool<DEFAULT> {
239 type Src = bool;
240
241 const TYPE_META: TypeMeta = TypeMeta::Static {
242 size: 1,
243 zero_copy: false,
244 };
245
246 fn size_of(_src: &Self::Src) -> WriteResult<usize> {
247 Ok(1)
248 }
249
250 fn write(mut writer: impl Writer, src: &Self::Src) -> WriteResult<()> {
251 writer.write(&[u8::from(*src)])?;
252 Ok(())
253 }
254}
255
256#[cfg(feature = "wincode")]
257pub fn create_buffer(
259 payer_address: &Pubkey,
260 buffer_address: &Pubkey,
261 authority_address: &Pubkey,
262 lamports: u64,
263 program_len: usize,
264) -> Result<Vec<Instruction>, InstructionError> {
265 Ok(vec![
266 system_instruction::create_account(
267 payer_address,
268 buffer_address,
269 lamports,
270 UpgradeableLoaderState::size_of_buffer(program_len) as u64,
271 &id(),
272 ),
273 Instruction::new_with_wincode(
274 id(),
275 &UpgradeableLoaderInstruction::InitializeBuffer,
276 vec![
277 AccountMeta::new(*buffer_address, false),
278 AccountMeta::new_readonly(*authority_address, false),
279 ],
280 ),
281 ])
282}
283
284#[cfg(feature = "wincode")]
285pub fn write(
288 buffer_address: &Pubkey,
289 authority_address: &Pubkey,
290 offset: u32,
291 bytes: Vec<u8>,
292) -> Instruction {
293 Instruction::new_with_wincode(
294 id(),
295 &UpgradeableLoaderInstruction::Write { offset, bytes },
296 vec![
297 AccountMeta::new(*buffer_address, false),
298 AccountMeta::new_readonly(*authority_address, true),
299 ],
300 )
301}
302
303#[cfg(feature = "wincode")]
304pub fn deploy_with_max_program_len(
308 payer_address: &Pubkey,
309 program_address: &Pubkey,
310 buffer_address: &Pubkey,
311 upgrade_authority_address: &Pubkey,
312 program_lamports: u64,
313 max_data_len: usize,
314 close_buffer: bool,
315) -> Result<Vec<Instruction>, InstructionError> {
316 let programdata_address = get_program_data_address(program_address);
317 Ok(vec![
318 system_instruction::create_account(
319 payer_address,
320 program_address,
321 program_lamports,
322 UpgradeableLoaderState::size_of_program() as u64,
323 &id(),
324 ),
325 Instruction::new_with_wincode(
326 id(),
327 &UpgradeableLoaderInstruction::DeployWithMaxDataLen {
328 max_data_len,
329 close_buffer,
330 },
331 vec![
332 AccountMeta::new(*payer_address, true),
333 AccountMeta::new(programdata_address, false),
334 AccountMeta::new(*program_address, false),
335 AccountMeta::new(*buffer_address, false),
336 AccountMeta::new_readonly(sysvar::rent::id(), false),
337 AccountMeta::new_readonly(sysvar::clock::id(), false),
338 AccountMeta::new_readonly(solana_sdk_ids::system_program::id(), false),
339 AccountMeta::new_readonly(*upgrade_authority_address, true),
340 ],
341 ),
342 ])
343}
344
345#[cfg(feature = "wincode")]
346pub fn upgrade(
348 program_address: &Pubkey,
349 buffer_address: &Pubkey,
350 authority_address: &Pubkey,
351 spill_address: &Pubkey,
352 close_buffer: bool,
353) -> Instruction {
354 let programdata_address = get_program_data_address(program_address);
355 Instruction::new_with_wincode(
356 id(),
357 &UpgradeableLoaderInstruction::Upgrade { close_buffer },
358 vec![
359 AccountMeta::new(programdata_address, false),
360 AccountMeta::new(*program_address, false),
361 AccountMeta::new(*buffer_address, false),
362 AccountMeta::new(*spill_address, false),
363 AccountMeta::new_readonly(sysvar::rent::id(), false),
364 AccountMeta::new_readonly(sysvar::clock::id(), false),
365 AccountMeta::new_readonly(*authority_address, true),
366 ],
367 )
368}
369
370pub fn is_upgrade_instruction(instruction_data: &[u8]) -> bool {
371 !instruction_data.is_empty() && 3 == instruction_data[0]
372}
373
374pub fn is_set_authority_instruction(instruction_data: &[u8]) -> bool {
375 !instruction_data.is_empty() && 4 == instruction_data[0]
376}
377
378pub fn is_close_instruction(instruction_data: &[u8]) -> bool {
379 !instruction_data.is_empty() && 5 == instruction_data[0]
380}
381
382pub fn is_set_authority_checked_instruction(instruction_data: &[u8]) -> bool {
383 !instruction_data.is_empty() && 7 == instruction_data[0]
384}
385
386#[cfg(feature = "wincode")]
387pub fn set_buffer_authority(
389 buffer_address: &Pubkey,
390 current_authority_address: &Pubkey,
391 new_authority_address: &Pubkey,
392) -> Instruction {
393 Instruction::new_with_wincode(
394 id(),
395 &UpgradeableLoaderInstruction::SetAuthority,
396 vec![
397 AccountMeta::new(*buffer_address, false),
398 AccountMeta::new_readonly(*current_authority_address, true),
399 AccountMeta::new_readonly(*new_authority_address, false),
400 ],
401 )
402}
403
404#[cfg(feature = "wincode")]
405pub fn set_buffer_authority_checked(
408 buffer_address: &Pubkey,
409 current_authority_address: &Pubkey,
410 new_authority_address: &Pubkey,
411) -> Instruction {
412 Instruction::new_with_wincode(
413 id(),
414 &UpgradeableLoaderInstruction::SetAuthorityChecked,
415 vec![
416 AccountMeta::new(*buffer_address, false),
417 AccountMeta::new_readonly(*current_authority_address, true),
418 AccountMeta::new_readonly(*new_authority_address, true),
419 ],
420 )
421}
422
423#[cfg(feature = "wincode")]
424pub fn set_upgrade_authority(
426 program_address: &Pubkey,
427 current_authority_address: &Pubkey,
428 new_authority_address: Option<&Pubkey>,
429) -> Instruction {
430 let programdata_address = get_program_data_address(program_address);
431
432 let mut metas = vec![
433 AccountMeta::new(programdata_address, false),
434 AccountMeta::new_readonly(*current_authority_address, true),
435 ];
436 if let Some(address) = new_authority_address {
437 metas.push(AccountMeta::new_readonly(*address, false));
438 }
439 Instruction::new_with_wincode(id(), &UpgradeableLoaderInstruction::SetAuthority, metas)
440}
441
442#[cfg(feature = "wincode")]
443pub fn set_upgrade_authority_checked(
446 program_address: &Pubkey,
447 current_authority_address: &Pubkey,
448 new_authority_address: &Pubkey,
449) -> Instruction {
450 let programdata_address = get_program_data_address(program_address);
451
452 let metas = vec![
453 AccountMeta::new(programdata_address, false),
454 AccountMeta::new_readonly(*current_authority_address, true),
455 AccountMeta::new_readonly(*new_authority_address, true),
456 ];
457 Instruction::new_with_wincode(
458 id(),
459 &UpgradeableLoaderInstruction::SetAuthorityChecked,
460 metas,
461 )
462}
463
464#[cfg(feature = "wincode")]
465pub fn close(
467 close_address: &Pubkey,
468 recipient_address: &Pubkey,
469 authority_address: &Pubkey,
470 tombstone: bool,
471) -> Instruction {
472 close_any(
473 close_address,
474 recipient_address,
475 Some(authority_address),
476 None,
477 tombstone,
478 )
479}
480
481#[cfg(feature = "wincode")]
482pub fn close_any(
484 close_address: &Pubkey,
485 recipient_address: &Pubkey,
486 authority_address: Option<&Pubkey>,
487 program_address: Option<&Pubkey>,
488 tombstone: bool,
489) -> Instruction {
490 let mut metas = vec![
491 AccountMeta::new(*close_address, false),
492 AccountMeta::new(*recipient_address, false),
493 ];
494 if let Some(authority_address) = authority_address {
495 metas.push(AccountMeta::new_readonly(*authority_address, true));
496 }
497 if let Some(program_address) = program_address {
498 metas.push(AccountMeta::new(*program_address, false));
499 }
500 Instruction::new_with_wincode(
501 id(),
502 &UpgradeableLoaderInstruction::Close { tombstone },
503 metas,
504 )
505}
506
507#[cfg(feature = "wincode")]
508pub fn extend_program(
515 program_address: &Pubkey,
516 payer_address: Option<&Pubkey>,
517 additional_bytes: u32,
518) -> Instruction {
519 let program_data_address = get_program_data_address(program_address);
520 let mut metas = vec![
521 AccountMeta::new(program_data_address, false),
522 AccountMeta::new(*program_address, false),
523 ];
524 if let Some(payer_address) = payer_address {
525 metas.push(AccountMeta::new_readonly(
526 solana_sdk_ids::system_program::id(),
527 false,
528 ));
529 metas.push(AccountMeta::new(*payer_address, true));
530 }
531 Instruction::new_with_wincode(
532 id(),
533 &UpgradeableLoaderInstruction::ExtendProgram { additional_bytes },
534 metas,
535 )
536}
537
538#[cfg(all(test, feature = "wincode"))]
539mod tests {
540 use {super::*, test_case::test_case};
541
542 fn assert_is_instruction<F>(
543 is_instruction_fn: F,
544 expected_instruction: UpgradeableLoaderInstruction,
545 ) where
546 F: Fn(&[u8]) -> bool,
547 {
548 let result = is_instruction_fn(
549 &wincode::serialize(&UpgradeableLoaderInstruction::InitializeBuffer).unwrap(),
550 );
551 let expected_result = matches!(
552 expected_instruction,
553 UpgradeableLoaderInstruction::InitializeBuffer
554 );
555 assert_eq!(expected_result, result);
556
557 let result = is_instruction_fn(
558 &wincode::serialize(&UpgradeableLoaderInstruction::Write {
559 offset: 0,
560 bytes: vec![],
561 })
562 .unwrap(),
563 );
564 let expected_result = matches!(
565 expected_instruction,
566 UpgradeableLoaderInstruction::Write {
567 offset: _,
568 bytes: _,
569 }
570 );
571 assert_eq!(expected_result, result);
572
573 let result = is_instruction_fn(
574 &wincode::serialize(&UpgradeableLoaderInstruction::DeployWithMaxDataLen {
575 max_data_len: 0,
576 close_buffer: true,
577 })
578 .unwrap(),
579 );
580 let expected_result = matches!(
581 expected_instruction,
582 UpgradeableLoaderInstruction::DeployWithMaxDataLen { .. }
583 );
584 assert_eq!(expected_result, result);
585
586 let result = is_instruction_fn(
587 &wincode::serialize(&UpgradeableLoaderInstruction::Upgrade { close_buffer: true })
588 .unwrap(),
589 );
590 let expected_result = matches!(
591 expected_instruction,
592 UpgradeableLoaderInstruction::Upgrade { .. }
593 );
594 assert_eq!(expected_result, result);
595
596 let result = is_instruction_fn(
597 &wincode::serialize(&UpgradeableLoaderInstruction::SetAuthority).unwrap(),
598 );
599 let expected_result = matches!(
600 expected_instruction,
601 UpgradeableLoaderInstruction::SetAuthority
602 );
603 assert_eq!(expected_result, result);
604
605 let result = is_instruction_fn(
606 &wincode::serialize(&UpgradeableLoaderInstruction::Close { tombstone: false }).unwrap(),
607 );
608 let expected_result = matches!(
609 expected_instruction,
610 UpgradeableLoaderInstruction::Close { .. }
611 );
612 assert_eq!(expected_result, result);
613 }
614
615 #[test]
616 fn test_is_set_authority_instruction() {
617 assert!(!is_set_authority_instruction(&[]));
618 assert_is_instruction(
619 is_set_authority_instruction,
620 UpgradeableLoaderInstruction::SetAuthority {},
621 );
622 }
623
624 #[test]
625 fn test_is_set_authority_checked_instruction() {
626 assert!(!is_set_authority_checked_instruction(&[]));
627 assert_is_instruction(
628 is_set_authority_checked_instruction,
629 UpgradeableLoaderInstruction::SetAuthorityChecked {},
630 );
631 }
632
633 #[test]
634 fn test_is_upgrade_instruction() {
635 assert!(!is_upgrade_instruction(&[]));
636 assert_is_instruction(
637 is_upgrade_instruction,
638 UpgradeableLoaderInstruction::Upgrade { close_buffer: true },
639 );
640 }
641
642 #[test_case(UpgradeableLoaderInstruction::InitializeBuffer)]
645 #[test_case(UpgradeableLoaderInstruction::Write { offset: 42, bytes: vec![1, 2, 3, 4, 5] })]
646 #[test_case(UpgradeableLoaderInstruction::Write { offset: 0, bytes: vec![] })]
647 #[test_case(UpgradeableLoaderInstruction::DeployWithMaxDataLen { max_data_len: 1_000_000, close_buffer: true })]
648 #[test_case(UpgradeableLoaderInstruction::DeployWithMaxDataLen { max_data_len: 0, close_buffer: false })]
649 #[test_case(UpgradeableLoaderInstruction::Upgrade { close_buffer: true })]
650 #[test_case(UpgradeableLoaderInstruction::Upgrade { close_buffer: false })]
651 #[test_case(UpgradeableLoaderInstruction::SetAuthority)]
652 #[test_case(UpgradeableLoaderInstruction::Close { tombstone: false })]
653 #[test_case(UpgradeableLoaderInstruction::Close { tombstone: true })]
654 #[test_case(UpgradeableLoaderInstruction::ExtendProgram { additional_bytes: 10_240 })]
655 #[test_case(UpgradeableLoaderInstruction::ExtendProgram { additional_bytes: 0 })]
656 #[test_case(UpgradeableLoaderInstruction::SetAuthorityChecked)]
657 fn wire_compat_bincode_vs_wincode(instr: UpgradeableLoaderInstruction) {
658 let bincode_bytes = bincode::serialize(&instr).unwrap();
659 let wincode_bytes = wincode::serialize(&instr).unwrap();
660 assert_eq!(bincode_bytes, wincode_bytes);
661
662 let from_bincode: UpgradeableLoaderInstruction =
663 bincode::deserialize(&bincode_bytes).unwrap();
664 let from_wincode: UpgradeableLoaderInstruction =
665 wincode::deserialize(&wincode_bytes).unwrap();
666 assert_eq!(from_bincode, instr);
667 assert_eq!(from_wincode, instr);
668 }
669
670 #[test]
673 fn legacy_deploy_decodes_close_buffer_as_true() {
674 let mut data = Vec::new();
675 data.extend_from_slice(&2u32.to_le_bytes()); data.extend_from_slice(&42u64.to_le_bytes()); let decoded: UpgradeableLoaderInstruction = wincode::deserialize(&data).unwrap();
678 assert_eq!(
679 decoded,
680 UpgradeableLoaderInstruction::DeployWithMaxDataLen {
681 max_data_len: 42,
682 close_buffer: true, }
684 );
685 }
686
687 #[test]
690 fn legacy_upgrade_decodes_close_buffer_as_true() {
691 let data = 3u32.to_le_bytes(); let decoded: UpgradeableLoaderInstruction = wincode::deserialize(&data).unwrap();
693 assert_eq!(
694 decoded,
695 UpgradeableLoaderInstruction::Upgrade {
696 close_buffer: true, }
698 );
699 }
700
701 #[test]
704 fn legacy_close_decodes_tombstone_as_false() {
705 let data = 5u32.to_le_bytes(); let decoded: UpgradeableLoaderInstruction = wincode::deserialize(&data).unwrap();
707 assert_eq!(
708 decoded,
709 UpgradeableLoaderInstruction::Close {
710 tombstone: false, }
712 );
713 }
714}