solana_loader_v3_interface/
instruction.rs

1//! Instructions for the upgradable BPF loader.
2
3#[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, loader_v4, sysvar},
9    solana_system_interface::instruction as system_instruction,
10};
11
12#[repr(u8)]
13#[cfg_attr(
14    feature = "serde",
15    derive(serde_derive::Deserialize, serde_derive::Serialize)
16)]
17#[derive(Debug, PartialEq, Eq, Clone)]
18pub enum UpgradeableLoaderInstruction {
19    /// Initialize a Buffer account.
20    ///
21    /// A Buffer account is an intermediary that once fully populated is used
22    /// with the `DeployWithMaxDataLen` instruction to populate the program's
23    /// ProgramData account.
24    ///
25    /// The `InitializeBuffer` instruction requires no signers and MUST be
26    /// included within the same Transaction as the system program's
27    /// `CreateAccount` instruction that creates the account being initialized.
28    /// Otherwise another party may initialize the account.
29    ///
30    /// # Account references
31    ///   0. `[writable]` source account to initialize.
32    ///   1. `[]` Buffer authority, optional, if omitted then the buffer will be
33    ///      immutable.
34    InitializeBuffer,
35
36    /// Write program data into a Buffer account.
37    ///
38    /// # Account references
39    ///   0. `[writable]` Buffer account to write program data to.
40    ///   1. `[signer]` Buffer authority
41    Write {
42        /// Offset at which to write the given bytes.
43        offset: u32,
44        /// Serialized program data
45        #[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]
46        bytes: Vec<u8>,
47    },
48
49    /// Deploy an executable program.
50    ///
51    /// A program consists of a Program and ProgramData account pair.
52    ///   - The Program account's address will serve as the program id for any
53    ///     instructions that execute this program.
54    ///   - The ProgramData account will remain mutable by the loader only and
55    ///     holds the program data and authority information.  The ProgramData
56    ///     account's address is derived from the Program account's address and
57    ///     created by the DeployWithMaxDataLen instruction.
58    ///
59    /// The ProgramData address is derived from the Program account's address as
60    /// follows:
61    ///
62    /// ```
63    /// # use solana_pubkey::Pubkey;
64    /// # use solana_sdk_ids::bpf_loader_upgradeable;
65    /// # let program_address = &[];
66    /// let (program_data_address, _) = Pubkey::find_program_address(
67    ///      &[program_address],
68    ///      &bpf_loader_upgradeable::id()
69    ///  );
70    /// ```
71    ///
72    /// The `DeployWithMaxDataLen` instruction does not require the ProgramData
73    /// account be a signer and therefore MUST be included within the same
74    /// Transaction as the system program's `CreateAccount` instruction that
75    /// creates the Program account. Otherwise another party may initialize the
76    /// account.
77    ///
78    /// # Account references
79    ///   0. `[writable, signer]` The payer account that will pay to create the
80    ///      ProgramData account.
81    ///   1. `[writable]` The uninitialized ProgramData account.
82    ///   2. `[writable]` The uninitialized Program account.
83    ///   3. `[writable]` The Buffer account where the program data has been
84    ///      written.  The buffer account's authority must match the program's
85    ///      authority
86    ///   4. `[]` Rent sysvar.
87    ///   5. `[]` Clock sysvar.
88    ///   6. `[]` System program (`solana_sdk_ids::system_program::id()`).
89    ///   7. `[signer]` The program's authority
90    DeployWithMaxDataLen {
91        /// Maximum length that the program can be upgraded to.
92        max_data_len: usize,
93    },
94
95    /// Upgrade a program.
96    ///
97    /// A program can be updated as long as the program's authority has not been
98    /// set to `None`.
99    ///
100    /// The Buffer account must contain sufficient lamports to fund the
101    /// ProgramData account to be rent-exempt, any additional lamports left over
102    /// will be transferred to the spill account, leaving the Buffer account
103    /// balance at zero.
104    ///
105    /// # Account references
106    ///   0. `[writable]` The ProgramData account.
107    ///   1. `[writable]` The Program account.
108    ///   2. `[writable]` The Buffer account where the program data has been
109    ///      written.  The buffer account's authority must match the program's
110    ///      authority
111    ///   3. `[writable]` The spill account.
112    ///   4. `[]` Rent sysvar.
113    ///   5. `[]` Clock sysvar.
114    ///   6. `[signer]` The program's authority.
115    Upgrade,
116
117    /// Set a new authority that is allowed to write the buffer or upgrade the
118    /// program.  To permanently make the buffer immutable or disable program
119    /// updates omit the new authority.
120    ///
121    /// # Account references
122    ///   0. `[writable]` The Buffer or ProgramData account to change the
123    ///      authority of.
124    ///   1. `[signer]` The current authority.
125    ///   2. `[]` The new authority, optional, if omitted then the program will
126    ///      not be upgradeable.
127    SetAuthority,
128
129    /// Closes an account owned by the upgradeable loader of all lamports and
130    /// withdraws all the lamports
131    ///
132    /// # Account references
133    ///   0. `[writable]` The account to close, if closing a program must be the
134    ///      ProgramData account.
135    ///   1. `[writable]` The account to deposit the closed account's lamports.
136    ///   2. `[signer]` The account's authority, Optional, required for
137    ///      initialized accounts.
138    ///   3. `[writable]` The associated Program account if the account to close
139    ///      is a ProgramData account.
140    Close,
141
142    /// Extend a program's ProgramData account by the specified number of bytes.
143    /// Only upgradeable programs can be extended.
144    ///
145    /// The payer account must contain sufficient lamports to fund the
146    /// ProgramData account to be rent-exempt. If the ProgramData account
147    /// balance is already sufficient to cover the rent exemption cost
148    /// for the extended bytes, the payer account is not required.
149    ///
150    /// # Account references
151    ///   0. `[writable]` The ProgramData account.
152    ///   1. `[writable]` The ProgramData account's associated Program account.
153    ///   2. `[]` System program (`solana_sdk::system_program::id()`), optional, used to transfer
154    ///      lamports from the payer to the ProgramData account.
155    ///   3. `[writable, signer]` The payer account, optional, that will pay
156    ///      necessary rent exemption costs for the increased storage size.
157    ExtendProgram {
158        /// Number of bytes to extend the program data.
159        additional_bytes: u32,
160    },
161
162    /// Set a new authority that is allowed to write the buffer or upgrade the
163    /// program.
164    ///
165    /// This instruction differs from SetAuthority in that the new authority is a
166    /// required signer.
167    ///
168    /// # Account references
169    ///   0. `[writable]` The Buffer or ProgramData account to change the
170    ///      authority of.
171    ///   1. `[signer]` The current authority.
172    ///   2. `[signer]` The new authority.
173    SetAuthorityChecked,
174
175    /// Migrate the program to loader-v4.
176    ///
177    /// # Account references
178    ///   0. `[writable]` The ProgramData account.
179    ///   1. `[writable]` The Program account.
180    ///   2. `[signer]` The current authority.
181    Migrate,
182
183    /// Extend a program's ProgramData account by the specified number of bytes.
184    /// Only upgradeable programs can be extended.
185    ///
186    /// This instruction differs from ExtendProgram in that the authority is a
187    /// required signer.
188    ///
189    /// # Account references
190    ///   0. `[writable]` The ProgramData account.
191    ///   1. `[writable]` The ProgramData account's associated Program account.
192    ///   2. `[signer]` The authority.
193    ///   3. `[]` System program (`solana_sdk::system_program::id()`), optional, used to transfer
194    ///      lamports from the payer to the ProgramData account.
195    ///   4. `[signer]` The payer account, optional, that will pay necessary rent exemption costs
196    ///      for the increased storage size.
197    ExtendProgramChecked {
198        /// Number of bytes to extend the program data.
199        additional_bytes: u32,
200    },
201}
202
203#[cfg(feature = "bincode")]
204/// Returns the instructions required to initialize a Buffer account.
205pub fn create_buffer(
206    payer_address: &Pubkey,
207    buffer_address: &Pubkey,
208    authority_address: &Pubkey,
209    lamports: u64,
210    program_len: usize,
211) -> Result<Vec<Instruction>, InstructionError> {
212    Ok(vec![
213        system_instruction::create_account(
214            payer_address,
215            buffer_address,
216            lamports,
217            UpgradeableLoaderState::size_of_buffer(program_len) as u64,
218            &id(),
219        ),
220        Instruction::new_with_bincode(
221            id(),
222            &UpgradeableLoaderInstruction::InitializeBuffer,
223            vec![
224                AccountMeta::new(*buffer_address, false),
225                AccountMeta::new_readonly(*authority_address, false),
226            ],
227        ),
228    ])
229}
230
231#[cfg(feature = "bincode")]
232/// Returns the instructions required to write a chunk of program data to a
233/// buffer account.
234pub fn write(
235    buffer_address: &Pubkey,
236    authority_address: &Pubkey,
237    offset: u32,
238    bytes: Vec<u8>,
239) -> Instruction {
240    Instruction::new_with_bincode(
241        id(),
242        &UpgradeableLoaderInstruction::Write { offset, bytes },
243        vec![
244            AccountMeta::new(*buffer_address, false),
245            AccountMeta::new_readonly(*authority_address, true),
246        ],
247    )
248}
249
250#[deprecated(since = "2.2.0", note = "Use loader-v4 instead")]
251#[cfg(feature = "bincode")]
252/// Returns the instructions required to deploy a program with a specified
253/// maximum program length.  The maximum length must be large enough to
254/// accommodate any future upgrades.
255pub fn deploy_with_max_program_len(
256    payer_address: &Pubkey,
257    program_address: &Pubkey,
258    buffer_address: &Pubkey,
259    upgrade_authority_address: &Pubkey,
260    program_lamports: u64,
261    max_data_len: usize,
262) -> Result<Vec<Instruction>, InstructionError> {
263    let programdata_address = get_program_data_address(program_address);
264    Ok(vec![
265        system_instruction::create_account(
266            payer_address,
267            program_address,
268            program_lamports,
269            UpgradeableLoaderState::size_of_program() as u64,
270            &id(),
271        ),
272        Instruction::new_with_bincode(
273            id(),
274            &UpgradeableLoaderInstruction::DeployWithMaxDataLen { max_data_len },
275            vec![
276                AccountMeta::new(*payer_address, true),
277                AccountMeta::new(programdata_address, false),
278                AccountMeta::new(*program_address, false),
279                AccountMeta::new(*buffer_address, false),
280                AccountMeta::new_readonly(sysvar::rent::id(), false),
281                AccountMeta::new_readonly(sysvar::clock::id(), false),
282                AccountMeta::new_readonly(solana_sdk_ids::system_program::id(), false),
283                AccountMeta::new_readonly(*upgrade_authority_address, true),
284            ],
285        ),
286    ])
287}
288
289#[cfg(feature = "bincode")]
290/// Returns the instructions required to upgrade a program.
291pub fn upgrade(
292    program_address: &Pubkey,
293    buffer_address: &Pubkey,
294    authority_address: &Pubkey,
295    spill_address: &Pubkey,
296) -> Instruction {
297    let programdata_address = get_program_data_address(program_address);
298    Instruction::new_with_bincode(
299        id(),
300        &UpgradeableLoaderInstruction::Upgrade,
301        vec![
302            AccountMeta::new(programdata_address, false),
303            AccountMeta::new(*program_address, false),
304            AccountMeta::new(*buffer_address, false),
305            AccountMeta::new(*spill_address, false),
306            AccountMeta::new_readonly(sysvar::rent::id(), false),
307            AccountMeta::new_readonly(sysvar::clock::id(), false),
308            AccountMeta::new_readonly(*authority_address, true),
309        ],
310    )
311}
312
313pub fn is_upgrade_instruction(instruction_data: &[u8]) -> bool {
314    !instruction_data.is_empty() && 3 == instruction_data[0]
315}
316
317pub fn is_set_authority_instruction(instruction_data: &[u8]) -> bool {
318    !instruction_data.is_empty() && 4 == instruction_data[0]
319}
320
321pub fn is_close_instruction(instruction_data: &[u8]) -> bool {
322    !instruction_data.is_empty() && 5 == instruction_data[0]
323}
324
325pub fn is_set_authority_checked_instruction(instruction_data: &[u8]) -> bool {
326    !instruction_data.is_empty() && 7 == instruction_data[0]
327}
328
329pub fn is_migrate_instruction(instruction_data: &[u8]) -> bool {
330    !instruction_data.is_empty() && 8 == instruction_data[0]
331}
332
333pub fn is_extend_program_checked_instruction(instruction_data: &[u8]) -> bool {
334    !instruction_data.is_empty() && 9 == instruction_data[0]
335}
336
337#[cfg(feature = "bincode")]
338/// Returns the instructions required to set a buffers's authority.
339pub fn set_buffer_authority(
340    buffer_address: &Pubkey,
341    current_authority_address: &Pubkey,
342    new_authority_address: &Pubkey,
343) -> Instruction {
344    Instruction::new_with_bincode(
345        id(),
346        &UpgradeableLoaderInstruction::SetAuthority,
347        vec![
348            AccountMeta::new(*buffer_address, false),
349            AccountMeta::new_readonly(*current_authority_address, true),
350            AccountMeta::new_readonly(*new_authority_address, false),
351        ],
352    )
353}
354
355#[cfg(feature = "bincode")]
356/// Returns the instructions required to set a buffers's authority. If using this instruction, the new authority
357/// must sign.
358pub fn set_buffer_authority_checked(
359    buffer_address: &Pubkey,
360    current_authority_address: &Pubkey,
361    new_authority_address: &Pubkey,
362) -> Instruction {
363    Instruction::new_with_bincode(
364        id(),
365        &UpgradeableLoaderInstruction::SetAuthorityChecked,
366        vec![
367            AccountMeta::new(*buffer_address, false),
368            AccountMeta::new_readonly(*current_authority_address, true),
369            AccountMeta::new_readonly(*new_authority_address, true),
370        ],
371    )
372}
373
374#[cfg(feature = "bincode")]
375/// Returns the instructions required to set a program's authority.
376pub fn set_upgrade_authority(
377    program_address: &Pubkey,
378    current_authority_address: &Pubkey,
379    new_authority_address: Option<&Pubkey>,
380) -> Instruction {
381    let programdata_address = get_program_data_address(program_address);
382
383    let mut metas = vec![
384        AccountMeta::new(programdata_address, false),
385        AccountMeta::new_readonly(*current_authority_address, true),
386    ];
387    if let Some(address) = new_authority_address {
388        metas.push(AccountMeta::new_readonly(*address, false));
389    }
390    Instruction::new_with_bincode(id(), &UpgradeableLoaderInstruction::SetAuthority, metas)
391}
392
393#[cfg(feature = "bincode")]
394/// Returns the instructions required to set a program's authority. If using this instruction, the new authority
395/// must sign.
396pub fn set_upgrade_authority_checked(
397    program_address: &Pubkey,
398    current_authority_address: &Pubkey,
399    new_authority_address: &Pubkey,
400) -> Instruction {
401    let programdata_address = get_program_data_address(program_address);
402
403    let metas = vec![
404        AccountMeta::new(programdata_address, false),
405        AccountMeta::new_readonly(*current_authority_address, true),
406        AccountMeta::new_readonly(*new_authority_address, true),
407    ];
408    Instruction::new_with_bincode(
409        id(),
410        &UpgradeableLoaderInstruction::SetAuthorityChecked,
411        metas,
412    )
413}
414
415#[cfg(feature = "bincode")]
416/// Returns the instructions required to close a buffer account
417pub fn close(
418    close_address: &Pubkey,
419    recipient_address: &Pubkey,
420    authority_address: &Pubkey,
421) -> Instruction {
422    close_any(
423        close_address,
424        recipient_address,
425        Some(authority_address),
426        None,
427    )
428}
429
430#[cfg(feature = "bincode")]
431/// Returns the instructions required to close program, buffer, or uninitialized account
432pub fn close_any(
433    close_address: &Pubkey,
434    recipient_address: &Pubkey,
435    authority_address: Option<&Pubkey>,
436    program_address: Option<&Pubkey>,
437) -> Instruction {
438    let mut metas = vec![
439        AccountMeta::new(*close_address, false),
440        AccountMeta::new(*recipient_address, false),
441    ];
442    if let Some(authority_address) = authority_address {
443        metas.push(AccountMeta::new_readonly(*authority_address, true));
444    }
445    if let Some(program_address) = program_address {
446        metas.push(AccountMeta::new(*program_address, false));
447    }
448    Instruction::new_with_bincode(id(), &UpgradeableLoaderInstruction::Close, metas)
449}
450
451#[cfg(feature = "bincode")]
452/// Returns the instruction required to extend the size of a program's
453/// executable data account
454pub fn extend_program(
455    program_address: &Pubkey,
456    payer_address: Option<&Pubkey>,
457    additional_bytes: u32,
458) -> Instruction {
459    let program_data_address = get_program_data_address(program_address);
460    let mut metas = vec![
461        AccountMeta::new(program_data_address, false),
462        AccountMeta::new(*program_address, false),
463    ];
464    if let Some(payer_address) = payer_address {
465        metas.push(AccountMeta::new_readonly(
466            solana_sdk_ids::system_program::id(),
467            false,
468        ));
469        metas.push(AccountMeta::new(*payer_address, true));
470    }
471    Instruction::new_with_bincode(
472        id(),
473        &UpgradeableLoaderInstruction::ExtendProgram { additional_bytes },
474        metas,
475    )
476}
477
478/// Returns the instructions required to migrate a program to loader-v4.
479#[cfg(feature = "bincode")]
480pub fn migrate_program(
481    programdata_address: &Pubkey,
482    program_address: &Pubkey,
483    authority: &Pubkey,
484) -> Instruction {
485    let accounts = vec![
486        AccountMeta::new(*programdata_address, false),
487        AccountMeta::new(*program_address, false),
488        AccountMeta::new_readonly(*authority, true),
489        AccountMeta::new_readonly(loader_v4::id(), false),
490    ];
491
492    Instruction::new_with_bincode(id(), &UpgradeableLoaderInstruction::Migrate, accounts)
493}
494
495/// Returns the instruction required to extend the size of a program's
496/// executable data account
497#[cfg(feature = "bincode")]
498pub fn extend_program_checked(
499    program_address: &Pubkey,
500    authority_address: &Pubkey,
501    payer_address: Option<&Pubkey>,
502    additional_bytes: u32,
503) -> Instruction {
504    let program_data_address = get_program_data_address(program_address);
505    let mut metas = vec![
506        AccountMeta::new(program_data_address, false),
507        AccountMeta::new(*program_address, false),
508        AccountMeta::new(*authority_address, true),
509    ];
510    if let Some(payer_address) = payer_address {
511        metas.push(AccountMeta::new_readonly(
512            solana_sdk_ids::system_program::id(),
513            false,
514        ));
515        metas.push(AccountMeta::new(*payer_address, true));
516    }
517    Instruction::new_with_bincode(
518        id(),
519        &UpgradeableLoaderInstruction::ExtendProgramChecked { additional_bytes },
520        metas,
521    )
522}
523
524#[cfg(test)]
525mod tests {
526    use super::*;
527
528    fn assert_is_instruction<F>(
529        is_instruction_fn: F,
530        expected_instruction: UpgradeableLoaderInstruction,
531    ) where
532        F: Fn(&[u8]) -> bool,
533    {
534        let result = is_instruction_fn(
535            &bincode::serialize(&UpgradeableLoaderInstruction::InitializeBuffer).unwrap(),
536        );
537        let expected_result = matches!(
538            expected_instruction,
539            UpgradeableLoaderInstruction::InitializeBuffer
540        );
541        assert_eq!(expected_result, result);
542
543        let result = is_instruction_fn(
544            &bincode::serialize(&UpgradeableLoaderInstruction::Write {
545                offset: 0,
546                bytes: vec![],
547            })
548            .unwrap(),
549        );
550        let expected_result = matches!(
551            expected_instruction,
552            UpgradeableLoaderInstruction::Write {
553                offset: _,
554                bytes: _,
555            }
556        );
557        assert_eq!(expected_result, result);
558
559        let result = is_instruction_fn(
560            &bincode::serialize(&UpgradeableLoaderInstruction::DeployWithMaxDataLen {
561                max_data_len: 0,
562            })
563            .unwrap(),
564        );
565        let expected_result = matches!(
566            expected_instruction,
567            UpgradeableLoaderInstruction::DeployWithMaxDataLen { max_data_len: _ }
568        );
569        assert_eq!(expected_result, result);
570
571        let result =
572            is_instruction_fn(&bincode::serialize(&UpgradeableLoaderInstruction::Upgrade).unwrap());
573        let expected_result = matches!(expected_instruction, UpgradeableLoaderInstruction::Upgrade);
574        assert_eq!(expected_result, result);
575
576        let result = is_instruction_fn(
577            &bincode::serialize(&UpgradeableLoaderInstruction::SetAuthority).unwrap(),
578        );
579        let expected_result = matches!(
580            expected_instruction,
581            UpgradeableLoaderInstruction::SetAuthority
582        );
583        assert_eq!(expected_result, result);
584
585        let result =
586            is_instruction_fn(&bincode::serialize(&UpgradeableLoaderInstruction::Close).unwrap());
587        let expected_result = matches!(expected_instruction, UpgradeableLoaderInstruction::Close);
588        assert_eq!(expected_result, result);
589    }
590
591    #[test]
592    fn test_is_set_authority_instruction() {
593        assert!(!is_set_authority_instruction(&[]));
594        assert_is_instruction(
595            is_set_authority_instruction,
596            UpgradeableLoaderInstruction::SetAuthority {},
597        );
598    }
599
600    #[test]
601    fn test_is_set_authority_checked_instruction() {
602        assert!(!is_set_authority_checked_instruction(&[]));
603        assert_is_instruction(
604            is_set_authority_checked_instruction,
605            UpgradeableLoaderInstruction::SetAuthorityChecked {},
606        );
607    }
608
609    #[test]
610    fn test_is_upgrade_instruction() {
611        assert!(!is_upgrade_instruction(&[]));
612        assert_is_instruction(
613            is_upgrade_instruction,
614            UpgradeableLoaderInstruction::Upgrade {},
615        );
616    }
617
618    #[test]
619    fn test_is_migrate_instruction() {
620        assert!(!is_migrate_instruction(&[]));
621        assert_is_instruction(
622            is_migrate_instruction,
623            UpgradeableLoaderInstruction::Migrate {},
624        );
625    }
626
627    #[test]
628    fn test_is_extend_program_checked_instruction() {
629        assert!(!is_extend_program_checked_instruction(&[]));
630        assert_is_instruction(
631            is_extend_program_checked_instruction,
632            UpgradeableLoaderInstruction::ExtendProgramChecked {
633                additional_bytes: 0,
634            },
635        );
636    }
637}