solana_loader_v4_interface/
instruction.rs

1//! Instructions for the v4 built-in loader program.
2#[cfg(feature = "bincode")]
3use {
4    solana_instruction::{AccountMeta, Instruction},
5    solana_pubkey::Pubkey,
6    solana_sdk_ids::loader_v4::id,
7};
8
9#[repr(u8)]
10#[cfg_attr(
11    feature = "serde",
12    derive(serde_derive::Deserialize, serde_derive::Serialize)
13)]
14#[derive(Debug, PartialEq, Eq, Clone)]
15pub enum LoaderV4Instruction {
16    /// Write ELF data into an undeployed program account.
17    ///
18    /// # Account references
19    ///   0. `[writable]` The program account to write to.
20    ///   1. `[signer]` The authority of the program.
21    Write {
22        /// Offset at which to write the given bytes.
23        offset: u32,
24        /// Serialized program data
25        #[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]
26        bytes: Vec<u8>,
27    },
28
29    /// Copy ELF data into an undeployed program account.
30    ///
31    /// # Account references
32    ///   0. `[writable]` The program account to write to.
33    ///   1. `[signer]` The authority of the program.
34    ///   2. `[]` The program account to copy from.
35    Copy {
36        /// Offset at which to write.
37        destination_offset: u32,
38        /// Offset at which to read.
39        source_offset: u32,
40        /// Amount of bytes to copy.
41        length: u32,
42    },
43
44    /// Changes the size of an undeployed program account.
45    ///
46    /// A program account is automatically initialized when its size is first increased.
47    /// In this initial truncate, this sets the authority needed for subsequent operations.
48    /// Decreasing to size zero closes the program account and resets it into an uninitialized state.
49    /// Closing the program requires a recipient account.
50    /// Providing additional lamports upfront might be necessary to reach rent exemption.
51    /// Superflous funds are transferred to the recipient account if provided.
52    ///
53    /// # Account references
54    ///   0. `[writable]` The program account to change the size of.
55    ///   1. `[signer]` The authority of the program.
56    ///   2. `[writable]` Optional, the recipient account.
57    SetProgramLength {
58        /// The new size after the operation.
59        new_size: u32,
60    },
61
62    /// Verify the data of a program account to be a valid ELF.
63    ///
64    /// If this succeeds the program becomes executable, and is ready to use.
65    /// A source program account can be provided to overwrite the data before deployment
66    /// in one step, instead retracting the program and writing to it and redeploying it.
67    /// The source program is truncated to zero (thus closed) and lamports necessary for
68    /// rent exemption are transferred, in case that the source was bigger than the program.
69    ///
70    /// # Account references
71    ///   0. `[writable]` The program account to deploy.
72    ///   1. `[signer]` The authority of the program.
73    ///   2. `[writable]` Optional, an undeployed source program account to take data and lamports from.
74    Deploy,
75
76    /// Undo the deployment of a program account.
77    ///
78    /// The program is no longer executable and goes into maintenance.
79    /// Necessary for writing data and truncating.
80    ///
81    /// # Account references
82    ///   0. `[writable]` The program account to retract.
83    ///   1. `[signer]` The authority of the program.
84    Retract,
85
86    /// Transfers the authority over a program account.
87    ///
88    /// # Account references
89    ///   0. `[writable]` The program account to change the authority of.
90    ///   1. `[signer]` The current authority of the program.
91    ///   2. `[signer]` The new authority of the program.
92    TransferAuthority,
93
94    /// Finalizes the program account, rendering it immutable.
95    ///
96    /// # Account references
97    ///   0. `[writable]` The program account to change the authority of.
98    ///   1. `[signer]` The current authority of the program.
99    ///   2. `[]` The next version of the program (can be itself).
100    Finalize,
101}
102
103pub fn is_write_instruction(instruction_data: &[u8]) -> bool {
104    !instruction_data.is_empty() && 0 == instruction_data[0]
105}
106
107pub fn is_copy_instruction(instruction_data: &[u8]) -> bool {
108    !instruction_data.is_empty() && 1 == instruction_data[0]
109}
110
111pub fn is_set_program_length_instruction(instruction_data: &[u8]) -> bool {
112    !instruction_data.is_empty() && 2 == instruction_data[0]
113}
114
115pub fn is_deploy_instruction(instruction_data: &[u8]) -> bool {
116    !instruction_data.is_empty() && 3 == instruction_data[0]
117}
118
119pub fn is_retract_instruction(instruction_data: &[u8]) -> bool {
120    !instruction_data.is_empty() && 4 == instruction_data[0]
121}
122
123pub fn is_transfer_authority_instruction(instruction_data: &[u8]) -> bool {
124    !instruction_data.is_empty() && 5 == instruction_data[0]
125}
126
127pub fn is_finalize_instruction(instruction_data: &[u8]) -> bool {
128    !instruction_data.is_empty() && 6 == instruction_data[0]
129}
130
131/// Returns the instructions required to initialize a program/buffer account.
132#[cfg(feature = "bincode")]
133pub fn create_buffer(
134    payer_address: &Pubkey,
135    buffer_address: &Pubkey,
136    lamports: u64,
137    authority: &Pubkey,
138    new_size: u32,
139    recipient_address: &Pubkey,
140) -> Vec<Instruction> {
141    vec![
142        solana_system_interface::instruction::create_account(
143            payer_address,
144            buffer_address,
145            lamports,
146            0,
147            &id(),
148        ),
149        set_program_length(buffer_address, authority, new_size, recipient_address),
150    ]
151}
152
153/// Returns the instructions required to set the length of the program account.
154#[cfg(feature = "bincode")]
155pub fn set_program_length(
156    program_address: &Pubkey,
157    authority: &Pubkey,
158    new_size: u32,
159    recipient_address: &Pubkey,
160) -> Instruction {
161    Instruction::new_with_bincode(
162        id(),
163        &LoaderV4Instruction::SetProgramLength { new_size },
164        vec![
165            AccountMeta::new(*program_address, false),
166            AccountMeta::new_readonly(*authority, true),
167            AccountMeta::new(*recipient_address, false),
168        ],
169    )
170}
171
172/// Returns the instructions required to write a chunk of program data to a
173/// buffer account.
174#[cfg(feature = "bincode")]
175pub fn write(
176    program_address: &Pubkey,
177    authority: &Pubkey,
178    offset: u32,
179    bytes: Vec<u8>,
180) -> Instruction {
181    Instruction::new_with_bincode(
182        id(),
183        &LoaderV4Instruction::Write { offset, bytes },
184        vec![
185            AccountMeta::new(*program_address, false),
186            AccountMeta::new_readonly(*authority, true),
187        ],
188    )
189}
190
191/// Returns the instructions required to copy a chunk of program data.
192#[cfg(feature = "bincode")]
193pub fn copy(
194    program_address: &Pubkey,
195    authority: &Pubkey,
196    source_address: &Pubkey,
197    destination_offset: u32,
198    source_offset: u32,
199    length: u32,
200) -> Instruction {
201    Instruction::new_with_bincode(
202        id(),
203        &LoaderV4Instruction::Copy {
204            destination_offset,
205            source_offset,
206            length,
207        },
208        vec![
209            AccountMeta::new(*program_address, false),
210            AccountMeta::new_readonly(*authority, true),
211            AccountMeta::new_readonly(*source_address, false),
212        ],
213    )
214}
215
216/// Returns the instructions required to deploy a program.
217#[cfg(feature = "bincode")]
218pub fn deploy(program_address: &Pubkey, authority: &Pubkey) -> Instruction {
219    Instruction::new_with_bincode(
220        id(),
221        &LoaderV4Instruction::Deploy,
222        vec![
223            AccountMeta::new(*program_address, false),
224            AccountMeta::new_readonly(*authority, true),
225        ],
226    )
227}
228
229/// Returns the instructions required to deploy a program using a buffer.
230#[cfg(feature = "bincode")]
231pub fn deploy_from_source(
232    program_address: &Pubkey,
233    authority: &Pubkey,
234    source_address: &Pubkey,
235) -> Instruction {
236    Instruction::new_with_bincode(
237        id(),
238        &LoaderV4Instruction::Deploy,
239        vec![
240            AccountMeta::new(*program_address, false),
241            AccountMeta::new_readonly(*authority, true),
242            AccountMeta::new(*source_address, false),
243        ],
244    )
245}
246
247/// Returns the instructions required to retract a program.
248#[cfg(feature = "bincode")]
249pub fn retract(program_address: &Pubkey, authority: &Pubkey) -> Instruction {
250    Instruction::new_with_bincode(
251        id(),
252        &LoaderV4Instruction::Retract,
253        vec![
254            AccountMeta::new(*program_address, false),
255            AccountMeta::new_readonly(*authority, true),
256        ],
257    )
258}
259
260/// Returns the instructions required to transfer authority over a program.
261#[cfg(feature = "bincode")]
262pub fn transfer_authority(
263    program_address: &Pubkey,
264    authority: &Pubkey,
265    new_authority: &Pubkey,
266) -> Instruction {
267    let accounts = vec![
268        AccountMeta::new(*program_address, false),
269        AccountMeta::new_readonly(*authority, true),
270        AccountMeta::new_readonly(*new_authority, true),
271    ];
272
273    Instruction::new_with_bincode(id(), &LoaderV4Instruction::TransferAuthority, accounts)
274}
275
276/// Returns the instructions required to finalize program.
277#[cfg(feature = "bincode")]
278pub fn finalize(
279    program_address: &Pubkey,
280    authority: &Pubkey,
281    next_version_program_address: &Pubkey,
282) -> Instruction {
283    let accounts = vec![
284        AccountMeta::new(*program_address, false),
285        AccountMeta::new_readonly(*authority, true),
286        AccountMeta::new_readonly(*next_version_program_address, false),
287    ];
288
289    Instruction::new_with_bincode(id(), &LoaderV4Instruction::Finalize, accounts)
290}
291
292#[cfg(test)]
293mod tests {
294    use {super::*, solana_sdk_ids::system_program};
295
296    #[test]
297    fn test_create_buffer_instruction() {
298        let payer = Pubkey::new_unique();
299        let program = Pubkey::new_unique();
300        let authority = Pubkey::new_unique();
301        let recipient = Pubkey::new_unique();
302        let instructions = create_buffer(&payer, &program, 123, &authority, 10, &recipient);
303        assert_eq!(instructions.len(), 2);
304        let instruction0 = &instructions[0];
305        assert_eq!(instruction0.program_id, system_program::id());
306        assert_eq!(instruction0.accounts.len(), 2);
307        assert_eq!(instruction0.accounts[0].pubkey, payer);
308        assert!(instruction0.accounts[0].is_writable);
309        assert!(instruction0.accounts[0].is_signer);
310        assert_eq!(instruction0.accounts[1].pubkey, program);
311        assert!(instruction0.accounts[1].is_writable);
312        assert!(instruction0.accounts[1].is_signer);
313
314        let instruction1 = &instructions[1];
315        assert!(is_set_program_length_instruction(&instruction1.data));
316        assert_eq!(instruction1.program_id, id());
317        assert_eq!(instruction1.accounts.len(), 3);
318        assert_eq!(instruction1.accounts[0].pubkey, program);
319        assert!(instruction1.accounts[0].is_writable);
320        assert!(!instruction1.accounts[0].is_signer);
321        assert_eq!(instruction1.accounts[1].pubkey, authority);
322        assert!(!instruction1.accounts[1].is_writable);
323        assert!(instruction1.accounts[1].is_signer);
324        assert_eq!(instruction1.accounts[2].pubkey, recipient);
325        assert!(instruction1.accounts[2].is_writable);
326        assert!(!instruction1.accounts[2].is_signer);
327    }
328
329    #[test]
330    fn test_write_instruction() {
331        let program = Pubkey::new_unique();
332        let authority = Pubkey::new_unique();
333        let instruction = write(&program, &authority, 123, vec![1, 2, 3, 4]);
334        assert!(is_write_instruction(&instruction.data));
335        assert_eq!(instruction.program_id, id());
336        assert_eq!(instruction.accounts.len(), 2);
337        assert_eq!(instruction.accounts[0].pubkey, program);
338        assert!(instruction.accounts[0].is_writable);
339        assert!(!instruction.accounts[0].is_signer);
340        assert_eq!(instruction.accounts[1].pubkey, authority);
341        assert!(!instruction.accounts[1].is_writable);
342        assert!(instruction.accounts[1].is_signer);
343    }
344
345    #[test]
346    fn test_copy_instruction() {
347        let program = Pubkey::new_unique();
348        let authority = Pubkey::new_unique();
349        let source = Pubkey::new_unique();
350        let instruction = copy(&program, &authority, &source, 1, 2, 3);
351        assert!(is_copy_instruction(&instruction.data));
352        assert_eq!(instruction.program_id, id());
353        assert_eq!(instruction.accounts.len(), 3);
354        assert_eq!(instruction.accounts[0].pubkey, program);
355        assert!(instruction.accounts[0].is_writable);
356        assert!(!instruction.accounts[0].is_signer);
357        assert_eq!(instruction.accounts[1].pubkey, authority);
358        assert!(!instruction.accounts[1].is_writable);
359        assert!(instruction.accounts[1].is_signer);
360        assert_eq!(instruction.accounts[2].pubkey, source);
361        assert!(!instruction.accounts[2].is_writable);
362        assert!(!instruction.accounts[2].is_signer);
363    }
364
365    #[test]
366    fn test_set_program_length_instruction() {
367        let program = Pubkey::new_unique();
368        let authority = Pubkey::new_unique();
369        let recipient = Pubkey::new_unique();
370        let instruction = set_program_length(&program, &authority, 10, &recipient);
371        assert!(is_set_program_length_instruction(&instruction.data));
372        assert_eq!(instruction.program_id, id());
373        assert_eq!(instruction.accounts.len(), 3);
374        assert_eq!(instruction.accounts[0].pubkey, program);
375        assert!(instruction.accounts[0].is_writable);
376        assert!(!instruction.accounts[0].is_signer);
377        assert_eq!(instruction.accounts[1].pubkey, authority);
378        assert!(!instruction.accounts[1].is_writable);
379        assert!(instruction.accounts[1].is_signer);
380        assert_eq!(instruction.accounts[2].pubkey, recipient);
381        assert!(instruction.accounts[2].is_writable);
382        assert!(!instruction.accounts[2].is_signer);
383    }
384
385    #[test]
386    fn test_deploy_instruction() {
387        let program = Pubkey::new_unique();
388        let authority = Pubkey::new_unique();
389        let instruction = deploy(&program, &authority);
390        assert!(is_deploy_instruction(&instruction.data));
391        assert_eq!(instruction.program_id, id());
392        assert_eq!(instruction.accounts.len(), 2);
393        assert_eq!(instruction.accounts[0].pubkey, program);
394        assert!(instruction.accounts[0].is_writable);
395        assert!(!instruction.accounts[0].is_signer);
396        assert_eq!(instruction.accounts[1].pubkey, authority);
397        assert!(!instruction.accounts[1].is_writable);
398        assert!(instruction.accounts[1].is_signer);
399    }
400
401    #[test]
402    fn test_deploy_from_source_instruction() {
403        let program = Pubkey::new_unique();
404        let authority = Pubkey::new_unique();
405        let source = Pubkey::new_unique();
406        let instruction = deploy_from_source(&program, &authority, &source);
407        assert!(is_deploy_instruction(&instruction.data));
408        assert_eq!(instruction.program_id, id());
409        assert_eq!(instruction.accounts.len(), 3);
410        assert_eq!(instruction.accounts[0].pubkey, program);
411        assert!(instruction.accounts[0].is_writable);
412        assert!(!instruction.accounts[0].is_signer);
413        assert_eq!(instruction.accounts[1].pubkey, authority);
414        assert!(!instruction.accounts[1].is_writable);
415        assert!(instruction.accounts[1].is_signer);
416        assert_eq!(instruction.accounts[2].pubkey, source);
417        assert!(instruction.accounts[2].is_writable);
418        assert!(!instruction.accounts[2].is_signer);
419    }
420
421    #[test]
422    fn test_retract_instruction() {
423        let program = Pubkey::new_unique();
424        let authority = Pubkey::new_unique();
425        let instruction = retract(&program, &authority);
426        assert!(is_retract_instruction(&instruction.data));
427        assert_eq!(instruction.program_id, id());
428        assert_eq!(instruction.accounts.len(), 2);
429        assert_eq!(instruction.accounts[0].pubkey, program);
430        assert!(instruction.accounts[0].is_writable);
431        assert!(!instruction.accounts[0].is_signer);
432        assert_eq!(instruction.accounts[1].pubkey, authority);
433        assert!(!instruction.accounts[1].is_writable);
434        assert!(instruction.accounts[1].is_signer);
435    }
436
437    #[test]
438    fn test_transfer_authority_instruction() {
439        let program = Pubkey::new_unique();
440        let authority = Pubkey::new_unique();
441        let new_authority = Pubkey::new_unique();
442        let instruction = transfer_authority(&program, &authority, &new_authority);
443        assert!(is_transfer_authority_instruction(&instruction.data));
444        assert_eq!(instruction.program_id, id());
445        assert_eq!(instruction.accounts.len(), 3);
446        assert_eq!(instruction.accounts[0].pubkey, program);
447        assert!(instruction.accounts[0].is_writable);
448        assert!(!instruction.accounts[0].is_signer);
449        assert_eq!(instruction.accounts[1].pubkey, authority);
450        assert!(!instruction.accounts[1].is_writable);
451        assert!(instruction.accounts[1].is_signer);
452        assert_eq!(instruction.accounts[2].pubkey, new_authority);
453        assert!(!instruction.accounts[2].is_writable);
454        assert!(instruction.accounts[2].is_signer);
455    }
456
457    #[test]
458    fn test_transfer_authority_finalize_instruction() {
459        let program = Pubkey::new_unique();
460        let authority = Pubkey::new_unique();
461        let next_version = Pubkey::new_unique();
462        let instruction = finalize(&program, &authority, &next_version);
463        assert!(is_finalize_instruction(&instruction.data));
464        assert_eq!(instruction.program_id, id());
465        assert_eq!(instruction.accounts.len(), 3);
466        assert_eq!(instruction.accounts[0].pubkey, program);
467        assert!(instruction.accounts[0].is_writable);
468        assert!(!instruction.accounts[0].is_signer);
469        assert_eq!(instruction.accounts[1].pubkey, authority);
470        assert!(!instruction.accounts[1].is_writable);
471        assert!(instruction.accounts[1].is_signer);
472        assert_eq!(instruction.accounts[2].pubkey, next_version);
473        assert!(!instruction.accounts[2].is_writable);
474        assert!(!instruction.accounts[2].is_signer);
475    }
476}