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