miraland_program/
loader_v4.rs

1//! The v4 built-in loader program.
2//!
3//! This is the loader of the program runtime v2.
4
5use crate::{
6    instruction::{AccountMeta, Instruction},
7    loader_v4_instruction::LoaderV4Instruction,
8    pubkey::Pubkey,
9    system_instruction,
10};
11
12crate::declare_id!("LoaderV411111111111111111111111111111111111");
13
14/// Cooldown before a program can be un-/redeployed again
15pub const DEPLOYMENT_COOLDOWN_IN_SLOTS: u64 = 750;
16
17#[repr(u64)]
18#[derive(Debug, PartialEq, Eq, Clone, Copy, AbiExample)]
19pub enum LoaderV4Status {
20    /// Program is in maintenance
21    Retracted,
22    /// Program is ready to be executed
23    Deployed,
24    /// Same as `Deployed`, but can not be retracted anymore
25    Finalized,
26}
27
28/// LoaderV4 account states
29#[repr(C)]
30#[derive(Debug, PartialEq, Eq, Clone, Copy, AbiExample)]
31pub struct LoaderV4State {
32    /// Slot in which the program was last deployed, retracted or initialized.
33    pub slot: u64,
34    /// Address of signer which can send program management instructions.
35    pub authority_address: Pubkey,
36    /// Deployment status.
37    pub status: LoaderV4Status,
38    // The raw program data follows this serialized structure in the
39    // account's data.
40}
41
42impl LoaderV4State {
43    /// Size of a serialized program account.
44    pub const fn program_data_offset() -> usize {
45        std::mem::size_of::<Self>()
46    }
47}
48
49pub fn is_write_instruction(instruction_data: &[u8]) -> bool {
50    !instruction_data.is_empty() && 0 == instruction_data[0]
51}
52
53pub fn is_truncate_instruction(instruction_data: &[u8]) -> bool {
54    !instruction_data.is_empty() && 1 == instruction_data[0]
55}
56
57pub fn is_deploy_instruction(instruction_data: &[u8]) -> bool {
58    !instruction_data.is_empty() && 2 == instruction_data[0]
59}
60
61pub fn is_retract_instruction(instruction_data: &[u8]) -> bool {
62    !instruction_data.is_empty() && 3 == instruction_data[0]
63}
64
65pub fn is_transfer_authority_instruction(instruction_data: &[u8]) -> bool {
66    !instruction_data.is_empty() && 4 == instruction_data[0]
67}
68
69/// Returns the instructions required to initialize a program/buffer account.
70pub fn create_buffer(
71    payer_address: &Pubkey,
72    buffer_address: &Pubkey,
73    lamports: u64,
74    authority: &Pubkey,
75    new_size: u32,
76    recipient_address: &Pubkey,
77) -> Vec<Instruction> {
78    vec![
79        system_instruction::create_account(payer_address, buffer_address, lamports, 0, &id()),
80        truncate_uninitialized(buffer_address, authority, new_size, recipient_address),
81    ]
82}
83
84/// Returns the instructions required to set the length of an uninitialized program account.
85/// This instruction will require the program account to also sign the transaction.
86pub fn truncate_uninitialized(
87    program_address: &Pubkey,
88    authority: &Pubkey,
89    new_size: u32,
90    recipient_address: &Pubkey,
91) -> Instruction {
92    Instruction::new_with_bincode(
93        id(),
94        &LoaderV4Instruction::Truncate { new_size },
95        vec![
96            AccountMeta::new(*program_address, true),
97            AccountMeta::new_readonly(*authority, true),
98            AccountMeta::new(*recipient_address, false),
99        ],
100    )
101}
102
103/// Returns the instructions required to set the length of the program account.
104pub fn truncate(
105    program_address: &Pubkey,
106    authority: &Pubkey,
107    new_size: u32,
108    recipient_address: &Pubkey,
109) -> Instruction {
110    Instruction::new_with_bincode(
111        id(),
112        &LoaderV4Instruction::Truncate { new_size },
113        vec![
114            AccountMeta::new(*program_address, false),
115            AccountMeta::new_readonly(*authority, true),
116            AccountMeta::new(*recipient_address, false),
117        ],
118    )
119}
120
121/// Returns the instructions required to write a chunk of program data to a
122/// buffer account.
123pub fn write(
124    program_address: &Pubkey,
125    authority: &Pubkey,
126    offset: u32,
127    bytes: Vec<u8>,
128) -> Instruction {
129    Instruction::new_with_bincode(
130        id(),
131        &LoaderV4Instruction::Write { offset, bytes },
132        vec![
133            AccountMeta::new(*program_address, false),
134            AccountMeta::new_readonly(*authority, true),
135        ],
136    )
137}
138
139/// Returns the instructions required to deploy a program.
140pub fn deploy(program_address: &Pubkey, authority: &Pubkey) -> Instruction {
141    Instruction::new_with_bincode(
142        id(),
143        &LoaderV4Instruction::Deploy,
144        vec![
145            AccountMeta::new(*program_address, false),
146            AccountMeta::new_readonly(*authority, true),
147        ],
148    )
149}
150
151/// Returns the instructions required to deploy a program using a buffer.
152pub fn deploy_from_source(
153    program_address: &Pubkey,
154    authority: &Pubkey,
155    source_address: &Pubkey,
156) -> Instruction {
157    Instruction::new_with_bincode(
158        id(),
159        &LoaderV4Instruction::Deploy,
160        vec![
161            AccountMeta::new(*program_address, false),
162            AccountMeta::new_readonly(*authority, true),
163            AccountMeta::new(*source_address, false),
164        ],
165    )
166}
167
168/// Returns the instructions required to retract a program.
169pub fn retract(program_address: &Pubkey, authority: &Pubkey) -> Instruction {
170    Instruction::new_with_bincode(
171        id(),
172        &LoaderV4Instruction::Retract,
173        vec![
174            AccountMeta::new(*program_address, false),
175            AccountMeta::new_readonly(*authority, true),
176        ],
177    )
178}
179
180/// Returns the instructions required to transfer authority over a program.
181pub fn transfer_authority(
182    program_address: &Pubkey,
183    authority: &Pubkey,
184    new_authority: Option<&Pubkey>,
185) -> Instruction {
186    let mut accounts = vec![
187        AccountMeta::new(*program_address, false),
188        AccountMeta::new_readonly(*authority, true),
189    ];
190
191    if let Some(new_auth) = new_authority {
192        accounts.push(AccountMeta::new_readonly(*new_auth, true));
193    }
194
195    Instruction::new_with_bincode(id(), &LoaderV4Instruction::TransferAuthority, accounts)
196}
197
198#[cfg(test)]
199mod tests {
200    use {super::*, crate::system_program, memoffset::offset_of};
201
202    #[test]
203    fn test_layout() {
204        assert_eq!(offset_of!(LoaderV4State, slot), 0x00);
205        assert_eq!(offset_of!(LoaderV4State, authority_address), 0x08);
206        assert_eq!(offset_of!(LoaderV4State, status), 0x28);
207        assert_eq!(LoaderV4State::program_data_offset(), 0x30);
208    }
209
210    #[test]
211    fn test_create_buffer_instruction() {
212        let payer = Pubkey::new_unique();
213        let program = Pubkey::new_unique();
214        let authority = Pubkey::new_unique();
215        let recipient = Pubkey::new_unique();
216        let instructions = create_buffer(&payer, &program, 123, &authority, 10, &recipient);
217        assert_eq!(instructions.len(), 2);
218        let instruction0 = &instructions[0];
219        assert_eq!(instruction0.program_id, system_program::id());
220        assert_eq!(instruction0.accounts.len(), 2);
221        assert_eq!(instruction0.accounts[0].pubkey, payer);
222        assert!(instruction0.accounts[0].is_writable);
223        assert!(instruction0.accounts[0].is_signer);
224        assert_eq!(instruction0.accounts[1].pubkey, program);
225        assert!(instruction0.accounts[1].is_writable);
226        assert!(instruction0.accounts[1].is_signer);
227
228        let instruction1 = &instructions[1];
229        assert!(is_truncate_instruction(&instruction1.data));
230        assert_eq!(instruction1.program_id, id());
231        assert_eq!(instruction1.accounts.len(), 3);
232        assert_eq!(instruction1.accounts[0].pubkey, program);
233        assert!(instruction1.accounts[0].is_writable);
234        assert!(instruction1.accounts[0].is_signer);
235        assert_eq!(instruction1.accounts[1].pubkey, authority);
236        assert!(!instruction1.accounts[1].is_writable);
237        assert!(instruction1.accounts[1].is_signer);
238        assert_eq!(instruction1.accounts[2].pubkey, recipient);
239        assert!(instruction1.accounts[2].is_writable);
240        assert!(!instruction1.accounts[2].is_signer);
241    }
242
243    #[test]
244    fn test_write_instruction() {
245        let program = Pubkey::new_unique();
246        let authority = Pubkey::new_unique();
247        let instruction = write(&program, &authority, 123, vec![1, 2, 3, 4]);
248        assert!(is_write_instruction(&instruction.data));
249        assert_eq!(instruction.program_id, id());
250        assert_eq!(instruction.accounts.len(), 2);
251        assert_eq!(instruction.accounts[0].pubkey, program);
252        assert!(instruction.accounts[0].is_writable);
253        assert!(!instruction.accounts[0].is_signer);
254        assert_eq!(instruction.accounts[1].pubkey, authority);
255        assert!(!instruction.accounts[1].is_writable);
256        assert!(instruction.accounts[1].is_signer);
257    }
258
259    #[test]
260    fn test_truncate_instruction() {
261        let program = Pubkey::new_unique();
262        let authority = Pubkey::new_unique();
263        let recipient = Pubkey::new_unique();
264        let instruction = truncate(&program, &authority, 10, &recipient);
265        assert!(is_truncate_instruction(&instruction.data));
266        assert_eq!(instruction.program_id, id());
267        assert_eq!(instruction.accounts.len(), 3);
268        assert_eq!(instruction.accounts[0].pubkey, program);
269        assert!(instruction.accounts[0].is_writable);
270        assert!(!instruction.accounts[0].is_signer);
271        assert_eq!(instruction.accounts[1].pubkey, authority);
272        assert!(!instruction.accounts[1].is_writable);
273        assert!(instruction.accounts[1].is_signer);
274        assert_eq!(instruction.accounts[2].pubkey, recipient);
275        assert!(instruction.accounts[2].is_writable);
276        assert!(!instruction.accounts[2].is_signer);
277    }
278
279    #[test]
280    fn test_deploy_instruction() {
281        let program = Pubkey::new_unique();
282        let authority = Pubkey::new_unique();
283        let instruction = deploy(&program, &authority);
284        assert!(is_deploy_instruction(&instruction.data));
285        assert_eq!(instruction.program_id, id());
286        assert_eq!(instruction.accounts.len(), 2);
287        assert_eq!(instruction.accounts[0].pubkey, program);
288        assert!(instruction.accounts[0].is_writable);
289        assert!(!instruction.accounts[0].is_signer);
290        assert_eq!(instruction.accounts[1].pubkey, authority);
291        assert!(!instruction.accounts[1].is_writable);
292        assert!(instruction.accounts[1].is_signer);
293    }
294
295    #[test]
296    fn test_deploy_from_source_instruction() {
297        let program = Pubkey::new_unique();
298        let authority = Pubkey::new_unique();
299        let source = Pubkey::new_unique();
300        let instruction = deploy_from_source(&program, &authority, &source);
301        assert!(is_deploy_instruction(&instruction.data));
302        assert_eq!(instruction.program_id, id());
303        assert_eq!(instruction.accounts.len(), 3);
304        assert_eq!(instruction.accounts[0].pubkey, program);
305        assert!(instruction.accounts[0].is_writable);
306        assert!(!instruction.accounts[0].is_signer);
307        assert_eq!(instruction.accounts[1].pubkey, authority);
308        assert!(!instruction.accounts[1].is_writable);
309        assert!(instruction.accounts[1].is_signer);
310        assert_eq!(instruction.accounts[2].pubkey, source);
311        assert!(instruction.accounts[2].is_writable);
312        assert!(!instruction.accounts[2].is_signer);
313    }
314
315    #[test]
316    fn test_retract_instruction() {
317        let program = Pubkey::new_unique();
318        let authority = Pubkey::new_unique();
319        let instruction = retract(&program, &authority);
320        assert!(is_retract_instruction(&instruction.data));
321        assert_eq!(instruction.program_id, id());
322        assert_eq!(instruction.accounts.len(), 2);
323        assert_eq!(instruction.accounts[0].pubkey, program);
324        assert!(instruction.accounts[0].is_writable);
325        assert!(!instruction.accounts[0].is_signer);
326        assert_eq!(instruction.accounts[1].pubkey, authority);
327        assert!(!instruction.accounts[1].is_writable);
328        assert!(instruction.accounts[1].is_signer);
329    }
330
331    #[test]
332    fn test_transfer_authority_instruction() {
333        let program = Pubkey::new_unique();
334        let authority = Pubkey::new_unique();
335        let new_authority = Pubkey::new_unique();
336        let instruction = transfer_authority(&program, &authority, Some(&new_authority));
337        assert!(is_transfer_authority_instruction(&instruction.data));
338        assert_eq!(instruction.program_id, id());
339        assert_eq!(instruction.accounts.len(), 3);
340        assert_eq!(instruction.accounts[0].pubkey, program);
341        assert!(instruction.accounts[0].is_writable);
342        assert!(!instruction.accounts[0].is_signer);
343        assert_eq!(instruction.accounts[1].pubkey, authority);
344        assert!(!instruction.accounts[1].is_writable);
345        assert!(instruction.accounts[1].is_signer);
346        assert_eq!(instruction.accounts[2].pubkey, new_authority);
347        assert!(!instruction.accounts[2].is_writable);
348        assert!(instruction.accounts[2].is_signer);
349    }
350
351    #[test]
352    fn test_transfer_authority_finalize_instruction() {
353        let program = Pubkey::new_unique();
354        let authority = Pubkey::new_unique();
355        let instruction = transfer_authority(&program, &authority, None);
356        assert!(is_transfer_authority_instruction(&instruction.data));
357        assert_eq!(instruction.program_id, id());
358        assert_eq!(instruction.accounts.len(), 2);
359        assert_eq!(instruction.accounts[0].pubkey, program);
360        assert!(instruction.accounts[0].is_writable);
361        assert!(!instruction.accounts[0].is_signer);
362        assert_eq!(instruction.accounts[1].pubkey, authority);
363        assert!(!instruction.accounts[1].is_writable);
364        assert!(instruction.accounts[1].is_signer);
365    }
366}