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