Skip to main content

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, sysvar},
9    solana_system_interface::instruction as system_instruction,
10};
11
12/// Minimum number of bytes for an `ExtendProgram` instruction.
13///
14/// After the SIMD-0431 feature gate is activated, `ExtendProgram` will
15/// reject requests smaller than this value, unless the program data
16/// account is within this many bytes of the max permitted data length of
17/// an account: 10 MiB.
18pub 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    /// Initialize a Buffer account.
28    ///
29    /// A Buffer account is an intermediary that once fully populated is used
30    /// with the `DeployWithMaxDataLen` instruction to populate the program's
31    /// ProgramData account.
32    ///
33    /// The `InitializeBuffer` instruction requires no signers and MUST be
34    /// included within the same Transaction as the system program's
35    /// `CreateAccount` instruction that creates the account being initialized.
36    /// Otherwise another party may initialize the account.
37    ///
38    /// # Account references
39    ///   0. `[writable]` source account to initialize.
40    ///   1. `[]` Buffer authority, optional, if omitted then the buffer will be
41    ///      immutable.
42    InitializeBuffer,
43
44    /// Write program data into a Buffer account.
45    ///
46    /// # Account references
47    ///   0. `[writable]` Buffer account to write program data to.
48    ///   1. `[signer]` Buffer authority
49    Write {
50        /// Offset at which to write the given bytes.
51        offset: u32,
52        /// Serialized program data
53        #[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]
54        bytes: Vec<u8>,
55    },
56
57    /// Deploy an executable program.
58    ///
59    /// A program consists of a Program and ProgramData account pair.
60    ///   - The Program account's address will serve as the program id for any
61    ///     instructions that execute this program.
62    ///   - The ProgramData account will remain mutable by the loader only and
63    ///     holds the program data and authority information.  The ProgramData
64    ///     account's address is derived from the Program account's address and
65    ///     created by the DeployWithMaxDataLen instruction.
66    ///
67    /// The ProgramData address is derived from the Program account's address as
68    /// follows:
69    ///
70    /// ```
71    /// # use solana_pubkey::Pubkey;
72    /// # use solana_sdk_ids::bpf_loader_upgradeable;
73    /// # let program_address = &[];
74    /// let (program_data_address, _) = Pubkey::find_program_address(
75    ///      &[program_address],
76    ///      &bpf_loader_upgradeable::id()
77    ///  );
78    /// ```
79    ///
80    /// The `DeployWithMaxDataLen` instruction does not require the ProgramData
81    /// account be a signer and therefore MUST be included within the same
82    /// Transaction as the system program's `CreateAccount` instruction that
83    /// creates the Program account. Otherwise another party may initialize the
84    /// account.
85    ///
86    /// # Account references
87    ///   0. `[writable, signer]` The payer account that will pay to create the
88    ///      ProgramData account.
89    ///   1. `[writable]` The uninitialized ProgramData account.
90    ///   2. `[writable]` The uninitialized Program account.
91    ///   3. `[writable]` The Buffer account where the program data has been
92    ///      written.  The buffer account's authority must match the program's
93    ///      authority
94    ///   4. `[]` Rent sysvar.
95    ///   5. `[]` Clock sysvar.
96    ///   6. `[]` System program (`solana_sdk_ids::system_program::id()`).
97    ///   7. `[signer]` The program's authority
98    DeployWithMaxDataLen {
99        /// Maximum length that the program can be upgraded to.
100        max_data_len: usize,
101    },
102
103    /// Upgrade a program.
104    ///
105    /// A program can be updated as long as the program's authority has not been
106    /// set to `None`.
107    ///
108    /// The Buffer account must contain sufficient lamports to fund the
109    /// ProgramData account to be rent-exempt, any additional lamports left over
110    /// will be transferred to the spill account, leaving the Buffer account
111    /// balance at zero.
112    ///
113    /// # Account references
114    ///   0. `[writable]` The ProgramData account.
115    ///   1. `[writable]` The Program account.
116    ///   2. `[writable]` The Buffer account where the program data has been
117    ///      written.  The buffer account's authority must match the program's
118    ///      authority
119    ///   3. `[writable]` The spill account.
120    ///   4. `[]` Rent sysvar.
121    ///   5. `[]` Clock sysvar.
122    ///   6. `[signer]` The program's authority.
123    Upgrade,
124
125    /// Set a new authority that is allowed to write the buffer or upgrade the
126    /// program.  To permanently make the buffer immutable or disable program
127    /// updates omit the new authority.
128    ///
129    /// # Account references
130    ///   0. `[writable]` The Buffer or ProgramData account to change the
131    ///      authority of.
132    ///   1. `[signer]` The current authority.
133    ///   2. `[]` The new authority, optional, if omitted then the program will
134    ///      not be upgradeable.
135    SetAuthority,
136
137    /// Closes an account owned by the upgradeable loader of all lamports and
138    /// withdraws all the lamports
139    ///
140    /// # Account references
141    ///   0. `[writable]` The account to close, if closing a program must be the
142    ///      ProgramData account.
143    ///   1. `[writable]` The account to deposit the closed account's lamports.
144    ///   2. `[signer]` The account's authority, Optional, required for
145    ///      initialized accounts.
146    ///   3. `[writable]` The associated Program account if the account to close
147    ///      is a ProgramData account.
148    Close,
149
150    /// Extend a program's ProgramData account by the specified number of bytes.
151    /// Only upgradeable programs can be extended.
152    ///
153    /// After the SIMD-0431 feature gate is activated, `additional_bytes`
154    /// must be at least [`MINIMUM_EXTEND_PROGRAM_BYTES`] (10 KiB).
155    /// The minimum does not apply when the program data account is
156    /// within [`MINIMUM_EXTEND_PROGRAM_BYTES`] of the max permitted
157    /// data length of an account: 10 MiB.
158    ///
159    /// The payer account must contain sufficient lamports to fund the
160    /// ProgramData account to be rent-exempt. If the ProgramData account
161    /// balance is already sufficient to cover the rent exemption cost
162    /// for the extended bytes, the payer account is not required.
163    ///
164    /// # Account references
165    ///   0. `[writable]` The ProgramData account.
166    ///   1. `[writable]` The ProgramData account's associated Program account.
167    ///   2. `[]` System program (`solana_sdk::system_program::id()`), optional, used to transfer
168    ///      lamports from the payer to the ProgramData account.
169    ///   3. `[writable, signer]` The payer account, optional, that will pay
170    ///      necessary rent exemption costs for the increased storage size.
171    ExtendProgram {
172        /// Number of bytes to extend the program data.
173        additional_bytes: u32,
174    },
175
176    /// Set a new authority that is allowed to write the buffer or upgrade the
177    /// program.
178    ///
179    /// This instruction differs from SetAuthority in that the new authority is a
180    /// required signer.
181    ///
182    /// # Account references
183    ///   0. `[writable]` The Buffer or ProgramData account to change the
184    ///      authority of.
185    ///   1. `[signer]` The current authority.
186    ///   2. `[signer]` The new authority.
187    SetAuthorityChecked,
188}
189
190#[cfg(feature = "bincode")]
191/// Returns the instructions required to initialize a Buffer account.
192pub 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")]
219/// Returns the instructions required to write a chunk of program data to a
220/// buffer account.
221pub 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")]
238/// Returns the instructions required to deploy a program with a specified
239/// maximum program length.  The maximum length must be large enough to
240/// accommodate any future upgrades.
241pub 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")]
276/// Returns the instructions required to upgrade a program.
277pub 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")]
316/// Returns the instructions required to set a buffers's authority.
317pub 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")]
334/// Returns the instructions required to set a buffers's authority. If using this instruction, the new authority
335/// must sign.
336pub 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")]
353/// Returns the instructions required to set a program's authority.
354pub 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")]
372/// Returns the instructions required to set a program's authority. If using this instruction, the new authority
373/// must sign.
374pub 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")]
394/// Returns the instructions required to close a buffer account
395pub 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")]
409/// Returns the instructions required to close program, buffer, or uninitialized account
410pub 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")]
430/// Returns the instruction required to extend the size of a program's
431/// executable data account.
432///
433/// After SIMD-0431 activation, `additional_bytes` must be at least
434/// [`MINIMUM_EXTEND_PROGRAM_BYTES`] unless the account is near the
435/// max permitted data length of an account: 10 MiB.
436pub 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}