clone_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    clone_solana_instruction::{error::InstructionError, AccountMeta, Instruction},
7    clone_solana_pubkey::Pubkey,
8    clone_solana_sdk_ids::{bpf_loader_upgradeable::id, loader_v4, sysvar},
9    clone_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 clone_solana_pubkey::Pubkey;
64    /// # use clone_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 (`clone_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 program's 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 (`clone_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
184#[cfg(feature = "bincode")]
185/// Returns the instructions required to initialize a Buffer account.
186pub fn create_buffer(
187    payer_address: &Pubkey,
188    buffer_address: &Pubkey,
189    authority_address: &Pubkey,
190    lamports: u64,
191    program_len: usize,
192) -> Result<Vec<Instruction>, InstructionError> {
193    Ok(vec![
194        system_instruction::create_account(
195            payer_address,
196            buffer_address,
197            lamports,
198            UpgradeableLoaderState::size_of_buffer(program_len) as u64,
199            &id(),
200        ),
201        Instruction::new_with_bincode(
202            id(),
203            &UpgradeableLoaderInstruction::InitializeBuffer,
204            vec![
205                AccountMeta::new(*buffer_address, false),
206                AccountMeta::new_readonly(*authority_address, false),
207            ],
208        ),
209    ])
210}
211
212#[cfg(feature = "bincode")]
213/// Returns the instructions required to write a chunk of program data to a
214/// buffer account.
215pub fn write(
216    buffer_address: &Pubkey,
217    authority_address: &Pubkey,
218    offset: u32,
219    bytes: Vec<u8>,
220) -> Instruction {
221    Instruction::new_with_bincode(
222        id(),
223        &UpgradeableLoaderInstruction::Write { offset, bytes },
224        vec![
225            AccountMeta::new(*buffer_address, false),
226            AccountMeta::new_readonly(*authority_address, true),
227        ],
228    )
229}
230
231#[deprecated(since = "2.2.0", note = "Use loader-v4 instead")]
232#[cfg(feature = "bincode")]
233/// Returns the instructions required to deploy a program with a specified
234/// maximum program length.  The maximum length must be large enough to
235/// accommodate any future upgrades.
236pub fn deploy_with_max_program_len(
237    payer_address: &Pubkey,
238    program_address: &Pubkey,
239    buffer_address: &Pubkey,
240    upgrade_authority_address: &Pubkey,
241    program_lamports: u64,
242    max_data_len: usize,
243) -> Result<Vec<Instruction>, InstructionError> {
244    let programdata_address = get_program_data_address(program_address);
245    Ok(vec![
246        system_instruction::create_account(
247            payer_address,
248            program_address,
249            program_lamports,
250            UpgradeableLoaderState::size_of_program() as u64,
251            &id(),
252        ),
253        Instruction::new_with_bincode(
254            id(),
255            &UpgradeableLoaderInstruction::DeployWithMaxDataLen { max_data_len },
256            vec![
257                AccountMeta::new(*payer_address, true),
258                AccountMeta::new(programdata_address, false),
259                AccountMeta::new(*program_address, false),
260                AccountMeta::new(*buffer_address, false),
261                AccountMeta::new_readonly(sysvar::rent::id(), false),
262                AccountMeta::new_readonly(sysvar::clock::id(), false),
263                AccountMeta::new_readonly(clone_solana_sdk_ids::system_program::id(), false),
264                AccountMeta::new_readonly(*upgrade_authority_address, true),
265            ],
266        ),
267    ])
268}
269
270#[cfg(feature = "bincode")]
271/// Returns the instructions required to upgrade a program.
272pub fn upgrade(
273    program_address: &Pubkey,
274    buffer_address: &Pubkey,
275    authority_address: &Pubkey,
276    spill_address: &Pubkey,
277) -> Instruction {
278    let programdata_address = get_program_data_address(program_address);
279    Instruction::new_with_bincode(
280        id(),
281        &UpgradeableLoaderInstruction::Upgrade,
282        vec![
283            AccountMeta::new(programdata_address, false),
284            AccountMeta::new(*program_address, false),
285            AccountMeta::new(*buffer_address, false),
286            AccountMeta::new(*spill_address, false),
287            AccountMeta::new_readonly(sysvar::rent::id(), false),
288            AccountMeta::new_readonly(sysvar::clock::id(), false),
289            AccountMeta::new_readonly(*authority_address, true),
290        ],
291    )
292}
293
294pub fn is_upgrade_instruction(instruction_data: &[u8]) -> bool {
295    !instruction_data.is_empty() && 3 == instruction_data[0]
296}
297
298pub fn is_set_authority_instruction(instruction_data: &[u8]) -> bool {
299    !instruction_data.is_empty() && 4 == instruction_data[0]
300}
301
302pub fn is_close_instruction(instruction_data: &[u8]) -> bool {
303    !instruction_data.is_empty() && 5 == instruction_data[0]
304}
305
306pub fn is_set_authority_checked_instruction(instruction_data: &[u8]) -> bool {
307    !instruction_data.is_empty() && 7 == instruction_data[0]
308}
309
310pub fn is_migrate_instruction(instruction_data: &[u8]) -> bool {
311    !instruction_data.is_empty() && 8 == instruction_data[0]
312}
313
314#[cfg(feature = "bincode")]
315/// Returns the instructions required to set a buffers's authority.
316pub fn set_buffer_authority(
317    buffer_address: &Pubkey,
318    current_authority_address: &Pubkey,
319    new_authority_address: &Pubkey,
320) -> Instruction {
321    Instruction::new_with_bincode(
322        id(),
323        &UpgradeableLoaderInstruction::SetAuthority,
324        vec![
325            AccountMeta::new(*buffer_address, false),
326            AccountMeta::new_readonly(*current_authority_address, true),
327            AccountMeta::new_readonly(*new_authority_address, false),
328        ],
329    )
330}
331
332#[cfg(feature = "bincode")]
333/// Returns the instructions required to set a buffers's authority. If using this instruction, the new authority
334/// must sign.
335pub fn set_buffer_authority_checked(
336    buffer_address: &Pubkey,
337    current_authority_address: &Pubkey,
338    new_authority_address: &Pubkey,
339) -> Instruction {
340    Instruction::new_with_bincode(
341        id(),
342        &UpgradeableLoaderInstruction::SetAuthorityChecked,
343        vec![
344            AccountMeta::new(*buffer_address, false),
345            AccountMeta::new_readonly(*current_authority_address, true),
346            AccountMeta::new_readonly(*new_authority_address, true),
347        ],
348    )
349}
350
351#[cfg(feature = "bincode")]
352/// Returns the instructions required to set a program's authority.
353pub fn set_upgrade_authority(
354    program_address: &Pubkey,
355    current_authority_address: &Pubkey,
356    new_authority_address: Option<&Pubkey>,
357) -> Instruction {
358    let programdata_address = get_program_data_address(program_address);
359
360    let mut metas = vec![
361        AccountMeta::new(programdata_address, false),
362        AccountMeta::new_readonly(*current_authority_address, true),
363    ];
364    if let Some(address) = new_authority_address {
365        metas.push(AccountMeta::new_readonly(*address, false));
366    }
367    Instruction::new_with_bincode(id(), &UpgradeableLoaderInstruction::SetAuthority, metas)
368}
369
370#[cfg(feature = "bincode")]
371/// Returns the instructions required to set a program's authority. If using this instruction, the new authority
372/// must sign.
373pub fn set_upgrade_authority_checked(
374    program_address: &Pubkey,
375    current_authority_address: &Pubkey,
376    new_authority_address: &Pubkey,
377) -> Instruction {
378    let programdata_address = get_program_data_address(program_address);
379
380    let metas = vec![
381        AccountMeta::new(programdata_address, false),
382        AccountMeta::new_readonly(*current_authority_address, true),
383        AccountMeta::new_readonly(*new_authority_address, true),
384    ];
385    Instruction::new_with_bincode(
386        id(),
387        &UpgradeableLoaderInstruction::SetAuthorityChecked,
388        metas,
389    )
390}
391
392#[cfg(feature = "bincode")]
393/// Returns the instructions required to close a buffer account
394pub fn close(
395    close_address: &Pubkey,
396    recipient_address: &Pubkey,
397    authority_address: &Pubkey,
398) -> Instruction {
399    close_any(
400        close_address,
401        recipient_address,
402        Some(authority_address),
403        None,
404    )
405}
406
407#[cfg(feature = "bincode")]
408/// Returns the instructions required to close program, buffer, or uninitialized account
409pub fn close_any(
410    close_address: &Pubkey,
411    recipient_address: &Pubkey,
412    authority_address: Option<&Pubkey>,
413    program_address: Option<&Pubkey>,
414) -> Instruction {
415    let mut metas = vec![
416        AccountMeta::new(*close_address, false),
417        AccountMeta::new(*recipient_address, false),
418    ];
419    if let Some(authority_address) = authority_address {
420        metas.push(AccountMeta::new_readonly(*authority_address, true));
421    }
422    if let Some(program_address) = program_address {
423        metas.push(AccountMeta::new(*program_address, false));
424    }
425    Instruction::new_with_bincode(id(), &UpgradeableLoaderInstruction::Close, metas)
426}
427
428#[cfg(feature = "bincode")]
429/// Returns the instruction required to extend the size of a program's
430/// executable data account
431pub fn extend_program(
432    program_address: &Pubkey,
433    payer_address: Option<&Pubkey>,
434    additional_bytes: u32,
435) -> Instruction {
436    let program_data_address = get_program_data_address(program_address);
437    let mut metas = vec![
438        AccountMeta::new(program_data_address, false),
439        AccountMeta::new(*program_address, false),
440    ];
441    if let Some(payer_address) = payer_address {
442        metas.push(AccountMeta::new_readonly(
443            clone_solana_sdk_ids::system_program::id(),
444            false,
445        ));
446        metas.push(AccountMeta::new(*payer_address, true));
447    }
448    Instruction::new_with_bincode(
449        id(),
450        &UpgradeableLoaderInstruction::ExtendProgram { additional_bytes },
451        metas,
452    )
453}
454
455/// Returns the instructions required to migrate a program to loader-v4.
456#[cfg(feature = "bincode")]
457pub fn migrate_program(
458    programdata_address: &Pubkey,
459    program_address: &Pubkey,
460    authority: &Pubkey,
461) -> Instruction {
462    let accounts = vec![
463        AccountMeta::new(*programdata_address, false),
464        AccountMeta::new(*program_address, false),
465        AccountMeta::new_readonly(*authority, true),
466        AccountMeta::new_readonly(loader_v4::id(), false),
467    ];
468
469    Instruction::new_with_bincode(id(), &UpgradeableLoaderInstruction::Migrate, accounts)
470}
471
472#[cfg(test)]
473mod tests {
474    use super::*;
475
476    fn assert_is_instruction<F>(
477        is_instruction_fn: F,
478        expected_instruction: UpgradeableLoaderInstruction,
479    ) where
480        F: Fn(&[u8]) -> bool,
481    {
482        let result = is_instruction_fn(
483            &bincode::serialize(&UpgradeableLoaderInstruction::InitializeBuffer).unwrap(),
484        );
485        let expected_result = matches!(
486            expected_instruction,
487            UpgradeableLoaderInstruction::InitializeBuffer
488        );
489        assert_eq!(expected_result, result);
490
491        let result = is_instruction_fn(
492            &bincode::serialize(&UpgradeableLoaderInstruction::Write {
493                offset: 0,
494                bytes: vec![],
495            })
496            .unwrap(),
497        );
498        let expected_result = matches!(
499            expected_instruction,
500            UpgradeableLoaderInstruction::Write {
501                offset: _,
502                bytes: _,
503            }
504        );
505        assert_eq!(expected_result, result);
506
507        let result = is_instruction_fn(
508            &bincode::serialize(&UpgradeableLoaderInstruction::DeployWithMaxDataLen {
509                max_data_len: 0,
510            })
511            .unwrap(),
512        );
513        let expected_result = matches!(
514            expected_instruction,
515            UpgradeableLoaderInstruction::DeployWithMaxDataLen { max_data_len: _ }
516        );
517        assert_eq!(expected_result, result);
518
519        let result =
520            is_instruction_fn(&bincode::serialize(&UpgradeableLoaderInstruction::Upgrade).unwrap());
521        let expected_result = matches!(expected_instruction, UpgradeableLoaderInstruction::Upgrade);
522        assert_eq!(expected_result, result);
523
524        let result = is_instruction_fn(
525            &bincode::serialize(&UpgradeableLoaderInstruction::SetAuthority).unwrap(),
526        );
527        let expected_result = matches!(
528            expected_instruction,
529            UpgradeableLoaderInstruction::SetAuthority
530        );
531        assert_eq!(expected_result, result);
532
533        let result =
534            is_instruction_fn(&bincode::serialize(&UpgradeableLoaderInstruction::Close).unwrap());
535        let expected_result = matches!(expected_instruction, UpgradeableLoaderInstruction::Close);
536        assert_eq!(expected_result, result);
537    }
538
539    #[test]
540    fn test_is_set_authority_instruction() {
541        assert!(!is_set_authority_instruction(&[]));
542        assert_is_instruction(
543            is_set_authority_instruction,
544            UpgradeableLoaderInstruction::SetAuthority {},
545        );
546    }
547
548    #[test]
549    fn test_is_set_authority_checked_instruction() {
550        assert!(!is_set_authority_checked_instruction(&[]));
551        assert_is_instruction(
552            is_set_authority_checked_instruction,
553            UpgradeableLoaderInstruction::SetAuthorityChecked {},
554        );
555    }
556
557    #[test]
558    fn test_is_upgrade_instruction() {
559        assert!(!is_upgrade_instruction(&[]));
560        assert_is_instruction(
561            is_upgrade_instruction,
562            UpgradeableLoaderInstruction::Upgrade {},
563        );
564    }
565
566    #[test]
567    fn test_is_migrate_instruction() {
568        assert!(!is_migrate_instruction(&[]));
569        assert_is_instruction(
570            is_migrate_instruction,
571            UpgradeableLoaderInstruction::Migrate {},
572        );
573    }
574}