cbe_program/
bpf_loader_upgradeable.rs

1//! An upgradeable BPF loader native program.
2//!
3//! The upgradeable BPF loader is responsible for deploying, upgrading, and
4//! executing BPF programs. The upgradeable loader allows a program's authority
5//! to update the program at any time. This ability break's the "code is law"
6//! contract the usually enforces the policy that once a program is on-chain it
7//! becomes immutable. Because of this, care should be taken before executing
8//! upgradeable programs which still have a functioning authority. For more
9//! information refer to the [`loader_upgradeable_instruction`] module.
10//!
11//! The `cbe program deploy` CLI command uses the
12//! upgradeable BPF loader. Calling `cbe program deploy --final` deploys a
13//! program that cannot be upgraded, but it does so by revoking the authority to
14//! upgrade, not by using the non-upgradeable loader.
15//!
16//! [`loader_upgradeable_instruction`]: crate::loader_upgradeable_instruction
17
18use crate::{
19    instruction::{AccountMeta, Instruction, InstructionError},
20    loader_upgradeable_instruction::UpgradeableLoaderInstruction,
21    pubkey::Pubkey,
22    system_instruction, sysvar,
23};
24
25crate::declare_id!("BPFLoaderUpgradeab1e11111111111111111111111");
26
27/// Upgradeable loader account states
28#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Copy, AbiExample)]
29pub enum UpgradeableLoaderState {
30    /// Account is not initialized.
31    Uninitialized,
32    /// A Buffer account.
33    Buffer {
34        /// Authority address
35        authority_address: Option<Pubkey>,
36        // The raw program data follows this serialized structure in the
37        // account's data.
38    },
39    /// An Program account.
40    Program {
41        /// Address of the ProgramData account.
42        programdata_address: Pubkey,
43    },
44    // A ProgramData account.
45    ProgramData {
46        /// Slot that the program was last modified.
47        slot: u64,
48        /// Address of the Program's upgrade authority.
49        upgrade_authority_address: Option<Pubkey>,
50        // The raw program data follows this serialized structure in the
51        // account's data.
52    },
53}
54impl UpgradeableLoaderState {
55    /// Size of a serialized program account.
56    pub const fn size_of_uninitialized() -> usize {
57        4 // see test_state_size_of_uninitialized
58    }
59
60    /// Size of a buffer account's serialized metadata.
61    pub const fn size_of_buffer_metadata() -> usize {
62        37 // see test_state_size_of_buffer_metadata
63    }
64
65    /// Size of a programdata account's serialized metadata.
66    pub const fn size_of_programdata_metadata() -> usize {
67        45 // see test_state_size_of_programdata_metadata
68    }
69
70    /// Size of a serialized program account.
71    pub const fn size_of_program() -> usize {
72        36 // see test_state_size_of_program
73    }
74
75    /// Size of a serialized buffer account.
76    pub const fn size_of_buffer(program_len: usize) -> usize {
77        Self::size_of_buffer_metadata().saturating_add(program_len)
78    }
79
80    /// Size of a serialized programdata account.
81    pub const fn size_of_programdata(program_len: usize) -> usize {
82        Self::size_of_programdata_metadata().saturating_add(program_len)
83    }
84
85    /// Length of a Buffer account's data.
86    #[deprecated(since = "1.11.0", note = "Please use `size_of_buffer` instead")]
87    pub fn buffer_len(program_len: usize) -> Result<usize, InstructionError> {
88        Ok(Self::size_of_buffer(program_len))
89    }
90
91    /// Offset into the Buffer account's data of the program bits.
92    #[deprecated(
93        since = "1.11.0",
94        note = "Please use `size_of_buffer_metadata` instead"
95    )]
96    pub fn buffer_data_offset() -> Result<usize, InstructionError> {
97        Ok(Self::size_of_buffer_metadata())
98    }
99
100    /// Length of a Program account's data.
101    #[deprecated(since = "1.11.0", note = "Please use `size_of_program` instead")]
102    pub fn program_len() -> Result<usize, InstructionError> {
103        Ok(Self::size_of_program())
104    }
105
106    /// Length of a ProgramData account's data.
107    #[deprecated(since = "1.11.0", note = "Please use `size_of_programdata` instead")]
108    pub fn programdata_len(program_len: usize) -> Result<usize, InstructionError> {
109        Ok(Self::size_of_programdata(program_len))
110    }
111
112    /// Offset into the ProgramData account's data of the program bits.
113    #[deprecated(
114        since = "1.11.0",
115        note = "Please use `size_of_programdata_metadata` instead"
116    )]
117    pub fn programdata_data_offset() -> Result<usize, InstructionError> {
118        Ok(Self::size_of_programdata_metadata())
119    }
120}
121
122/// Returns the instructions required to initialize a Buffer account.
123pub fn create_buffer(
124    payer_address: &Pubkey,
125    buffer_address: &Pubkey,
126    authority_address: &Pubkey,
127    scoobies: u64,
128    program_len: usize,
129) -> Result<Vec<Instruction>, InstructionError> {
130    Ok(vec![
131        system_instruction::create_account(
132            payer_address,
133            buffer_address,
134            scoobies,
135            UpgradeableLoaderState::size_of_buffer(program_len) as u64,
136            &id(),
137        ),
138        Instruction::new_with_bincode(
139            id(),
140            &UpgradeableLoaderInstruction::InitializeBuffer,
141            vec![
142                AccountMeta::new(*buffer_address, false),
143                AccountMeta::new_readonly(*authority_address, false),
144            ],
145        ),
146    ])
147}
148
149/// Returns the instructions required to write a chunk of program data to a
150/// buffer account.
151pub fn write(
152    buffer_address: &Pubkey,
153    authority_address: &Pubkey,
154    offset: u32,
155    bytes: Vec<u8>,
156) -> Instruction {
157    Instruction::new_with_bincode(
158        id(),
159        &UpgradeableLoaderInstruction::Write { offset, bytes },
160        vec![
161            AccountMeta::new(*buffer_address, false),
162            AccountMeta::new_readonly(*authority_address, true),
163        ],
164    )
165}
166
167/// Returns the instructions required to deploy a program with a specified
168/// maximum program length.  The maximum length must be large enough to
169/// accommodate any future upgrades.
170pub fn deploy_with_max_program_len(
171    payer_address: &Pubkey,
172    program_address: &Pubkey,
173    buffer_address: &Pubkey,
174    upgrade_authority_address: &Pubkey,
175    program_scoobies: u64,
176    max_data_len: usize,
177) -> Result<Vec<Instruction>, InstructionError> {
178    let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id());
179    Ok(vec![
180        system_instruction::create_account(
181            payer_address,
182            program_address,
183            program_scoobies,
184            UpgradeableLoaderState::size_of_program() as u64,
185            &id(),
186        ),
187        Instruction::new_with_bincode(
188            id(),
189            &UpgradeableLoaderInstruction::DeployWithMaxDataLen { max_data_len },
190            vec![
191                AccountMeta::new(*payer_address, true),
192                AccountMeta::new(programdata_address, false),
193                AccountMeta::new(*program_address, false),
194                AccountMeta::new(*buffer_address, false),
195                AccountMeta::new_readonly(sysvar::rent::id(), false),
196                AccountMeta::new_readonly(sysvar::clock::id(), false),
197                AccountMeta::new_readonly(crate::system_program::id(), false),
198                AccountMeta::new_readonly(*upgrade_authority_address, true),
199            ],
200        ),
201    ])
202}
203
204/// Returns the instructions required to upgrade a program.
205pub fn upgrade(
206    program_address: &Pubkey,
207    buffer_address: &Pubkey,
208    authority_address: &Pubkey,
209    spill_address: &Pubkey,
210) -> Instruction {
211    let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id());
212    Instruction::new_with_bincode(
213        id(),
214        &UpgradeableLoaderInstruction::Upgrade,
215        vec![
216            AccountMeta::new(programdata_address, false),
217            AccountMeta::new(*program_address, false),
218            AccountMeta::new(*buffer_address, false),
219            AccountMeta::new(*spill_address, false),
220            AccountMeta::new_readonly(sysvar::rent::id(), false),
221            AccountMeta::new_readonly(sysvar::clock::id(), false),
222            AccountMeta::new_readonly(*authority_address, true),
223        ],
224    )
225}
226
227pub fn is_upgrade_instruction(instruction_data: &[u8]) -> bool {
228    !instruction_data.is_empty() && 3 == instruction_data[0]
229}
230
231pub fn is_set_authority_instruction(instruction_data: &[u8]) -> bool {
232    !instruction_data.is_empty() && 4 == instruction_data[0]
233}
234
235pub fn is_close_instruction(instruction_data: &[u8]) -> bool {
236    !instruction_data.is_empty() && 5 == instruction_data[0]
237}
238
239pub fn is_set_authority_checked_instruction(instruction_data: &[u8]) -> bool {
240    !instruction_data.is_empty() && 7 == instruction_data[0]
241}
242
243/// Returns the instructions required to set a buffers's authority.
244pub fn set_buffer_authority(
245    buffer_address: &Pubkey,
246    current_authority_address: &Pubkey,
247    new_authority_address: &Pubkey,
248) -> Instruction {
249    Instruction::new_with_bincode(
250        id(),
251        &UpgradeableLoaderInstruction::SetAuthority,
252        vec![
253            AccountMeta::new(*buffer_address, false),
254            AccountMeta::new_readonly(*current_authority_address, true),
255            AccountMeta::new_readonly(*new_authority_address, false),
256        ],
257    )
258}
259
260/// Returns the instructions required to set a buffers's authority. If using this instruction, the new authority
261/// must sign.
262pub fn set_buffer_authority_checked(
263    buffer_address: &Pubkey,
264    current_authority_address: &Pubkey,
265    new_authority_address: &Pubkey,
266) -> Instruction {
267    Instruction::new_with_bincode(
268        id(),
269        &UpgradeableLoaderInstruction::SetAuthorityChecked,
270        vec![
271            AccountMeta::new(*buffer_address, false),
272            AccountMeta::new_readonly(*current_authority_address, true),
273            AccountMeta::new_readonly(*new_authority_address, true),
274        ],
275    )
276}
277
278/// Returns the instructions required to set a program's authority.
279pub fn set_upgrade_authority(
280    program_address: &Pubkey,
281    current_authority_address: &Pubkey,
282    new_authority_address: Option<&Pubkey>,
283) -> Instruction {
284    let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id());
285
286    let mut metas = vec![
287        AccountMeta::new(programdata_address, false),
288        AccountMeta::new_readonly(*current_authority_address, true),
289    ];
290    if let Some(address) = new_authority_address {
291        metas.push(AccountMeta::new_readonly(*address, false));
292    }
293    Instruction::new_with_bincode(id(), &UpgradeableLoaderInstruction::SetAuthority, metas)
294}
295
296/// Returns the instructions required to set a program's authority. If using this instruction, the new authority
297/// must sign.
298pub fn set_upgrade_authority_checked(
299    program_address: &Pubkey,
300    current_authority_address: &Pubkey,
301    new_authority_address: &Pubkey,
302) -> Instruction {
303    let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id());
304
305    let metas = vec![
306        AccountMeta::new(programdata_address, false),
307        AccountMeta::new_readonly(*current_authority_address, true),
308        AccountMeta::new_readonly(*new_authority_address, true),
309    ];
310    Instruction::new_with_bincode(
311        id(),
312        &UpgradeableLoaderInstruction::SetAuthorityChecked,
313        metas,
314    )
315}
316
317/// Returns the instructions required to close a buffer account
318pub fn close(
319    close_address: &Pubkey,
320    recipient_address: &Pubkey,
321    authority_address: &Pubkey,
322) -> Instruction {
323    close_any(
324        close_address,
325        recipient_address,
326        Some(authority_address),
327        None,
328    )
329}
330
331/// Returns the instructions required to close program, buffer, or uninitialized account
332pub fn close_any(
333    close_address: &Pubkey,
334    recipient_address: &Pubkey,
335    authority_address: Option<&Pubkey>,
336    program_address: Option<&Pubkey>,
337) -> Instruction {
338    let mut metas = vec![
339        AccountMeta::new(*close_address, false),
340        AccountMeta::new(*recipient_address, false),
341    ];
342    if let Some(authority_address) = authority_address {
343        metas.push(AccountMeta::new_readonly(*authority_address, true));
344    }
345    if let Some(program_address) = program_address {
346        metas.push(AccountMeta::new(*program_address, false));
347    }
348    Instruction::new_with_bincode(id(), &UpgradeableLoaderInstruction::Close, metas)
349}
350
351/// Returns the instruction required to extend the size of a program's
352/// executable data account
353pub fn extend_program(
354    program_address: &Pubkey,
355    payer_address: Option<&Pubkey>,
356    additional_bytes: u32,
357) -> Instruction {
358    let (program_data_address, _) =
359        Pubkey::find_program_address(&[program_address.as_ref()], &id());
360    let mut metas = vec![
361        AccountMeta::new(program_data_address, false),
362        AccountMeta::new(*program_address, false),
363    ];
364    if let Some(payer_address) = payer_address {
365        metas.push(AccountMeta::new_readonly(
366            crate::system_program::id(),
367            false,
368        ));
369        metas.push(AccountMeta::new(*payer_address, true));
370    }
371    Instruction::new_with_bincode(
372        id(),
373        &UpgradeableLoaderInstruction::ExtendProgram { additional_bytes },
374        metas,
375    )
376}
377
378#[cfg(test)]
379mod tests {
380    use {super::*, bincode::serialized_size};
381
382    #[test]
383    fn test_state_size_of_uninitialized() {
384        let buffer_state = UpgradeableLoaderState::Uninitialized;
385        let size = serialized_size(&buffer_state).unwrap();
386        assert_eq!(UpgradeableLoaderState::size_of_uninitialized() as u64, size);
387    }
388
389    #[test]
390    fn test_state_size_of_buffer_metadata() {
391        let buffer_state = UpgradeableLoaderState::Buffer {
392            authority_address: Some(Pubkey::default()),
393        };
394        let size = serialized_size(&buffer_state).unwrap();
395        assert_eq!(
396            UpgradeableLoaderState::size_of_buffer_metadata() as u64,
397            size
398        );
399    }
400
401    #[test]
402    fn test_state_size_of_programdata_metadata() {
403        let programdata_state = UpgradeableLoaderState::ProgramData {
404            upgrade_authority_address: Some(Pubkey::default()),
405            slot: 0,
406        };
407        let size = serialized_size(&programdata_state).unwrap();
408        assert_eq!(
409            UpgradeableLoaderState::size_of_programdata_metadata() as u64,
410            size
411        );
412    }
413
414    #[test]
415    fn test_state_size_of_program() {
416        let program_state = UpgradeableLoaderState::Program {
417            programdata_address: Pubkey::default(),
418        };
419        let size = serialized_size(&program_state).unwrap();
420        assert_eq!(UpgradeableLoaderState::size_of_program() as u64, size);
421    }
422
423    #[test]
424    #[allow(deprecated)]
425    fn test_account_lengths() {
426        assert_eq!(
427            4,
428            serialized_size(&UpgradeableLoaderState::Uninitialized).unwrap()
429        );
430        assert_eq!(36, UpgradeableLoaderState::program_len().unwrap());
431        assert_eq!(
432            45,
433            UpgradeableLoaderState::programdata_data_offset().unwrap()
434        );
435        assert_eq!(
436            45 + 42,
437            UpgradeableLoaderState::programdata_len(42).unwrap()
438        );
439    }
440
441    fn assert_is_instruction<F>(
442        is_instruction_fn: F,
443        expected_instruction: UpgradeableLoaderInstruction,
444    ) where
445        F: Fn(&[u8]) -> bool,
446    {
447        let result = is_instruction_fn(
448            &bincode::serialize(&UpgradeableLoaderInstruction::InitializeBuffer).unwrap(),
449        );
450        let expected_result = matches!(
451            expected_instruction,
452            UpgradeableLoaderInstruction::InitializeBuffer
453        );
454        assert_eq!(expected_result, result);
455
456        let result = is_instruction_fn(
457            &bincode::serialize(&UpgradeableLoaderInstruction::Write {
458                offset: 0,
459                bytes: vec![],
460            })
461            .unwrap(),
462        );
463        let expected_result = matches!(
464            expected_instruction,
465            UpgradeableLoaderInstruction::Write {
466                offset: _,
467                bytes: _,
468            }
469        );
470        assert_eq!(expected_result, result);
471
472        let result = is_instruction_fn(
473            &bincode::serialize(&UpgradeableLoaderInstruction::DeployWithMaxDataLen {
474                max_data_len: 0,
475            })
476            .unwrap(),
477        );
478        let expected_result = matches!(
479            expected_instruction,
480            UpgradeableLoaderInstruction::DeployWithMaxDataLen { max_data_len: _ }
481        );
482        assert_eq!(expected_result, result);
483
484        let result =
485            is_instruction_fn(&bincode::serialize(&UpgradeableLoaderInstruction::Upgrade).unwrap());
486        let expected_result = matches!(expected_instruction, UpgradeableLoaderInstruction::Upgrade);
487        assert_eq!(expected_result, result);
488
489        let result = is_instruction_fn(
490            &bincode::serialize(&UpgradeableLoaderInstruction::SetAuthority).unwrap(),
491        );
492        let expected_result = matches!(
493            expected_instruction,
494            UpgradeableLoaderInstruction::SetAuthority
495        );
496        assert_eq!(expected_result, result);
497
498        let result =
499            is_instruction_fn(&bincode::serialize(&UpgradeableLoaderInstruction::Close).unwrap());
500        let expected_result = matches!(expected_instruction, UpgradeableLoaderInstruction::Close);
501        assert_eq!(expected_result, result);
502    }
503
504    #[test]
505    fn test_is_set_authority_instruction() {
506        assert!(!is_set_authority_instruction(&[]));
507        assert_is_instruction(
508            is_set_authority_instruction,
509            UpgradeableLoaderInstruction::SetAuthority {},
510        );
511    }
512
513    #[test]
514    fn test_is_set_authority_checked_instruction() {
515        assert!(!is_set_authority_checked_instruction(&[]));
516        assert_is_instruction(
517            is_set_authority_checked_instruction,
518            UpgradeableLoaderInstruction::SetAuthorityChecked {},
519        );
520    }
521
522    #[test]
523    fn test_is_upgrade_instruction() {
524        assert!(!is_upgrade_instruction(&[]));
525        assert_is_instruction(
526            is_upgrade_instruction,
527            UpgradeableLoaderInstruction::Upgrade {},
528        );
529    }
530}