gemachain_program/
bpf_loader_upgradeable.rs

1//! @brief An Upgradeable Gemachain BPF loader.
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 `loader_upgradeable_instruction.rs`
10
11use crate::{
12    instruction::{AccountMeta, Instruction, InstructionError},
13    loader_upgradeable_instruction::UpgradeableLoaderInstruction,
14    pubkey::Pubkey,
15    system_instruction, sysvar,
16};
17use bincode::serialized_size;
18
19crate::declare_id!("BPFLoaderUpgradeab1e11111111111111111111111");
20
21/// Upgradeable loader account states
22#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Copy, AbiExample)]
23pub enum UpgradeableLoaderState {
24    /// Account is not initialized.
25    Uninitialized,
26    /// A Buffer account.
27    Buffer {
28        /// Authority address
29        authority_address: Option<Pubkey>,
30        // The raw program data follows this serialized structure in the
31        // account's data.
32    },
33    /// An Program account.
34    Program {
35        /// Address of the ProgramData account.
36        programdata_address: Pubkey,
37    },
38    // A ProgramData account.
39    ProgramData {
40        /// Slot that the program was last modified.
41        slot: u64,
42        /// Address of the Program's upgrade authority.
43        upgrade_authority_address: Option<Pubkey>,
44        // The raw program data follows this serialized structure in the
45        // account's data.
46    },
47}
48impl UpgradeableLoaderState {
49    /// Length of an buffer account's data.
50    pub fn buffer_len(program_len: usize) -> Result<usize, InstructionError> {
51        Ok(serialized_size(&Self::Buffer {
52            authority_address: Some(Pubkey::default()),
53        })
54        .map(|len| len as usize)
55        .map_err(|_| InstructionError::InvalidInstructionData)?
56        .saturating_add(program_len))
57    }
58    /// Offset into the ProgramData account's data of the program bits.
59    pub fn buffer_data_offset() -> Result<usize, InstructionError> {
60        Self::buffer_len(0)
61    }
62    /// Length of an executable account's data.
63    pub fn program_len() -> Result<usize, InstructionError> {
64        serialized_size(&Self::Program {
65            programdata_address: Pubkey::default(),
66        })
67        .map(|len| len as usize)
68        .map_err(|_| InstructionError::InvalidInstructionData)
69    }
70    /// Length of a ProgramData account's data.
71    pub fn programdata_len(program_len: usize) -> Result<usize, InstructionError> {
72        Ok(serialized_size(&Self::ProgramData {
73            slot: 0,
74            upgrade_authority_address: Some(Pubkey::default()),
75        })
76        .map(|len| len as usize)
77        .map_err(|_| InstructionError::InvalidInstructionData)?
78        .saturating_add(program_len))
79    }
80    /// Offset into the ProgramData account's data of the program bits.
81    pub fn programdata_data_offset() -> Result<usize, InstructionError> {
82        Self::programdata_len(0)
83    }
84}
85
86/// Returns the instructions required to initialize a Buffer account.
87pub fn create_buffer(
88    payer_address: &Pubkey,
89    buffer_address: &Pubkey,
90    authority_address: &Pubkey,
91    carats: u64,
92    program_len: usize,
93) -> Result<Vec<Instruction>, InstructionError> {
94    Ok(vec![
95        system_instruction::create_account(
96            payer_address,
97            buffer_address,
98            carats,
99            UpgradeableLoaderState::buffer_len(program_len)? as u64,
100            &id(),
101        ),
102        Instruction::new_with_bincode(
103            id(),
104            &UpgradeableLoaderInstruction::InitializeBuffer,
105            vec![
106                AccountMeta::new(*buffer_address, false),
107                AccountMeta::new_readonly(*authority_address, false),
108            ],
109        ),
110    ])
111}
112
113/// Returns the instructions required to write a chunk of program data to a
114/// buffer account.
115pub fn write(
116    buffer_address: &Pubkey,
117    authority_address: &Pubkey,
118    offset: u32,
119    bytes: Vec<u8>,
120) -> Instruction {
121    Instruction::new_with_bincode(
122        id(),
123        &UpgradeableLoaderInstruction::Write { offset, bytes },
124        vec![
125            AccountMeta::new(*buffer_address, false),
126            AccountMeta::new_readonly(*authority_address, true),
127        ],
128    )
129}
130
131/// Returns the instructions required to deploy a program with a specified
132/// maximum program length.  The maximum length must be large enough to
133/// accommodate any future upgrades.
134pub fn deploy_with_max_program_len(
135    payer_address: &Pubkey,
136    program_address: &Pubkey,
137    buffer_address: &Pubkey,
138    upgrade_authority_address: &Pubkey,
139    program_carats: u64,
140    max_data_len: usize,
141) -> Result<Vec<Instruction>, InstructionError> {
142    let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id());
143    Ok(vec![
144        system_instruction::create_account(
145            payer_address,
146            program_address,
147            program_carats,
148            UpgradeableLoaderState::program_len()? as u64,
149            &id(),
150        ),
151        Instruction::new_with_bincode(
152            id(),
153            &UpgradeableLoaderInstruction::DeployWithMaxDataLen { max_data_len },
154            vec![
155                AccountMeta::new(*payer_address, true),
156                AccountMeta::new(programdata_address, false),
157                AccountMeta::new(*program_address, false),
158                AccountMeta::new(*buffer_address, false),
159                AccountMeta::new_readonly(sysvar::rent::id(), false),
160                AccountMeta::new_readonly(sysvar::clock::id(), false),
161                AccountMeta::new_readonly(crate::system_program::id(), false),
162                AccountMeta::new_readonly(*upgrade_authority_address, true),
163            ],
164        ),
165    ])
166}
167
168/// Returns the instructions required to upgrade a program.
169pub fn upgrade(
170    program_address: &Pubkey,
171    buffer_address: &Pubkey,
172    authority_address: &Pubkey,
173    spill_address: &Pubkey,
174) -> Instruction {
175    let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id());
176    Instruction::new_with_bincode(
177        id(),
178        &UpgradeableLoaderInstruction::Upgrade,
179        vec![
180            AccountMeta::new(programdata_address, false),
181            AccountMeta::new(*program_address, false),
182            AccountMeta::new(*buffer_address, false),
183            AccountMeta::new(*spill_address, false),
184            AccountMeta::new_readonly(sysvar::rent::id(), false),
185            AccountMeta::new_readonly(sysvar::clock::id(), false),
186            AccountMeta::new_readonly(*authority_address, true),
187        ],
188    )
189}
190
191pub fn is_upgrade_instruction(instruction_data: &[u8]) -> bool {
192    !instruction_data.is_empty() && 3 == instruction_data[0]
193}
194
195pub fn is_set_authority_instruction(instruction_data: &[u8]) -> bool {
196    !instruction_data.is_empty() && 4 == instruction_data[0]
197}
198
199pub fn is_close_instruction(instruction_data: &[u8]) -> bool {
200    !instruction_data.is_empty() && 5 == instruction_data[0]
201}
202
203/// Returns the instructions required to set a buffers's authority.
204pub fn set_buffer_authority(
205    buffer_address: &Pubkey,
206    current_authority_address: &Pubkey,
207    new_authority_address: &Pubkey,
208) -> Instruction {
209    Instruction::new_with_bincode(
210        id(),
211        &UpgradeableLoaderInstruction::SetAuthority,
212        vec![
213            AccountMeta::new(*buffer_address, false),
214            AccountMeta::new_readonly(*current_authority_address, true),
215            AccountMeta::new_readonly(*new_authority_address, false),
216        ],
217    )
218}
219
220/// Returns the instructions required to set a program's authority.
221pub fn set_upgrade_authority(
222    program_address: &Pubkey,
223    current_authority_address: &Pubkey,
224    new_authority_address: Option<&Pubkey>,
225) -> Instruction {
226    let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id());
227
228    let mut metas = vec![
229        AccountMeta::new(programdata_address, false),
230        AccountMeta::new_readonly(*current_authority_address, true),
231    ];
232    if let Some(address) = new_authority_address {
233        metas.push(AccountMeta::new_readonly(*address, false));
234    }
235    Instruction::new_with_bincode(id(), &UpgradeableLoaderInstruction::SetAuthority, metas)
236}
237
238/// Returns the instructions required to close a buffer account
239pub fn close(
240    close_address: &Pubkey,
241    recipient_address: &Pubkey,
242    authority_address: &Pubkey,
243) -> Instruction {
244    close_any(
245        close_address,
246        recipient_address,
247        Some(authority_address),
248        None,
249    )
250}
251
252/// Returns the instructions required to close program, buffer, or uninitialized account
253pub fn close_any(
254    close_address: &Pubkey,
255    recipient_address: &Pubkey,
256    authority_address: Option<&Pubkey>,
257    program_address: Option<&Pubkey>,
258) -> Instruction {
259    let mut metas = vec![
260        AccountMeta::new(*close_address, false),
261        AccountMeta::new(*recipient_address, false),
262    ];
263    if let Some(authority_address) = authority_address {
264        metas.push(AccountMeta::new(*authority_address, true));
265    }
266    if let Some(program_address) = program_address {
267        metas.push(AccountMeta::new(*program_address, false));
268    }
269    Instruction::new_with_bincode(id(), &UpgradeableLoaderInstruction::Close, metas)
270}
271
272#[cfg(test)]
273mod tests {
274    use super::*;
275
276    #[test]
277    fn test_account_lengths() {
278        assert_eq!(
279            4,
280            serialized_size(&UpgradeableLoaderState::Uninitialized).unwrap()
281        );
282        assert_eq!(36, UpgradeableLoaderState::program_len().unwrap());
283        assert_eq!(
284            45,
285            UpgradeableLoaderState::programdata_data_offset().unwrap()
286        );
287        assert_eq!(
288            45 + 42,
289            UpgradeableLoaderState::programdata_len(42).unwrap()
290        );
291    }
292
293    fn assert_is_instruction<F>(
294        is_instruction_fn: F,
295        expected_instruction: UpgradeableLoaderInstruction,
296    ) where
297        F: Fn(&[u8]) -> bool,
298    {
299        let result = is_instruction_fn(
300            &bincode::serialize(&UpgradeableLoaderInstruction::InitializeBuffer).unwrap(),
301        );
302        let expected_result = matches!(
303            expected_instruction,
304            UpgradeableLoaderInstruction::InitializeBuffer
305        );
306        assert_eq!(expected_result, result);
307
308        let result = is_instruction_fn(
309            &bincode::serialize(&UpgradeableLoaderInstruction::Write {
310                offset: 0,
311                bytes: vec![],
312            })
313            .unwrap(),
314        );
315        let expected_result = matches!(
316            expected_instruction,
317            UpgradeableLoaderInstruction::Write {
318                offset: _,
319                bytes: _,
320            }
321        );
322        assert_eq!(expected_result, result);
323
324        let result = is_instruction_fn(
325            &bincode::serialize(&UpgradeableLoaderInstruction::DeployWithMaxDataLen {
326                max_data_len: 0,
327            })
328            .unwrap(),
329        );
330        let expected_result = matches!(
331            expected_instruction,
332            UpgradeableLoaderInstruction::DeployWithMaxDataLen { max_data_len: _ }
333        );
334        assert_eq!(expected_result, result);
335
336        let result =
337            is_instruction_fn(&bincode::serialize(&UpgradeableLoaderInstruction::Upgrade).unwrap());
338        let expected_result = matches!(expected_instruction, UpgradeableLoaderInstruction::Upgrade);
339        assert_eq!(expected_result, result);
340
341        let result = is_instruction_fn(
342            &bincode::serialize(&UpgradeableLoaderInstruction::SetAuthority).unwrap(),
343        );
344        let expected_result = matches!(
345            expected_instruction,
346            UpgradeableLoaderInstruction::SetAuthority
347        );
348        assert_eq!(expected_result, result);
349
350        let result =
351            is_instruction_fn(&bincode::serialize(&UpgradeableLoaderInstruction::Close).unwrap());
352        let expected_result = matches!(expected_instruction, UpgradeableLoaderInstruction::Close);
353        assert_eq!(expected_result, result);
354    }
355
356    #[test]
357    fn test_is_set_authority_instruction() {
358        assert!(!is_set_authority_instruction(&[]));
359        assert_is_instruction(
360            is_set_authority_instruction,
361            UpgradeableLoaderInstruction::SetAuthority {},
362        );
363    }
364
365    #[test]
366    fn test_is_upgrade_instruction() {
367        assert!(!is_upgrade_instruction(&[]));
368        assert_is_instruction(
369            is_upgrade_instruction,
370            UpgradeableLoaderInstruction::Upgrade {},
371        );
372    }
373}