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