1#[cfg(feature = "bincode")]
4use {
5 crate::{get_program_data_address, state::UpgradeableLoaderState},
6 clone_solana_instruction::{error::InstructionError, AccountMeta, Instruction},
7 clone_solana_pubkey::Pubkey,
8 clone_solana_sdk_ids::{bpf_loader_upgradeable::id, loader_v4, sysvar},
9 clone_solana_system_interface::instruction as system_instruction,
10};
11
12#[repr(u8)]
13#[cfg_attr(
14 feature = "serde",
15 derive(serde_derive::Deserialize, serde_derive::Serialize)
16)]
17#[derive(Debug, PartialEq, Eq, Clone)]
18pub enum UpgradeableLoaderInstruction {
19 InitializeBuffer,
35
36 Write {
42 offset: u32,
44 #[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]
46 bytes: Vec<u8>,
47 },
48
49 DeployWithMaxDataLen {
91 max_data_len: usize,
93 },
94
95 Upgrade,
116
117 SetAuthority,
128
129 Close,
141
142 ExtendProgram {
158 additional_bytes: u32,
160 },
161
162 SetAuthorityChecked,
174
175 Migrate,
182}
183
184#[cfg(feature = "bincode")]
185pub fn create_buffer(
187 payer_address: &Pubkey,
188 buffer_address: &Pubkey,
189 authority_address: &Pubkey,
190 lamports: u64,
191 program_len: usize,
192) -> Result<Vec<Instruction>, InstructionError> {
193 Ok(vec![
194 system_instruction::create_account(
195 payer_address,
196 buffer_address,
197 lamports,
198 UpgradeableLoaderState::size_of_buffer(program_len) as u64,
199 &id(),
200 ),
201 Instruction::new_with_bincode(
202 id(),
203 &UpgradeableLoaderInstruction::InitializeBuffer,
204 vec![
205 AccountMeta::new(*buffer_address, false),
206 AccountMeta::new_readonly(*authority_address, false),
207 ],
208 ),
209 ])
210}
211
212#[cfg(feature = "bincode")]
213pub fn write(
216 buffer_address: &Pubkey,
217 authority_address: &Pubkey,
218 offset: u32,
219 bytes: Vec<u8>,
220) -> Instruction {
221 Instruction::new_with_bincode(
222 id(),
223 &UpgradeableLoaderInstruction::Write { offset, bytes },
224 vec![
225 AccountMeta::new(*buffer_address, false),
226 AccountMeta::new_readonly(*authority_address, true),
227 ],
228 )
229}
230
231#[deprecated(since = "2.2.0", note = "Use loader-v4 instead")]
232#[cfg(feature = "bincode")]
233pub fn deploy_with_max_program_len(
237 payer_address: &Pubkey,
238 program_address: &Pubkey,
239 buffer_address: &Pubkey,
240 upgrade_authority_address: &Pubkey,
241 program_lamports: u64,
242 max_data_len: usize,
243) -> Result<Vec<Instruction>, InstructionError> {
244 let programdata_address = get_program_data_address(program_address);
245 Ok(vec![
246 system_instruction::create_account(
247 payer_address,
248 program_address,
249 program_lamports,
250 UpgradeableLoaderState::size_of_program() as u64,
251 &id(),
252 ),
253 Instruction::new_with_bincode(
254 id(),
255 &UpgradeableLoaderInstruction::DeployWithMaxDataLen { max_data_len },
256 vec![
257 AccountMeta::new(*payer_address, true),
258 AccountMeta::new(programdata_address, false),
259 AccountMeta::new(*program_address, false),
260 AccountMeta::new(*buffer_address, false),
261 AccountMeta::new_readonly(sysvar::rent::id(), false),
262 AccountMeta::new_readonly(sysvar::clock::id(), false),
263 AccountMeta::new_readonly(clone_solana_sdk_ids::system_program::id(), false),
264 AccountMeta::new_readonly(*upgrade_authority_address, true),
265 ],
266 ),
267 ])
268}
269
270#[cfg(feature = "bincode")]
271pub fn upgrade(
273 program_address: &Pubkey,
274 buffer_address: &Pubkey,
275 authority_address: &Pubkey,
276 spill_address: &Pubkey,
277) -> Instruction {
278 let programdata_address = get_program_data_address(program_address);
279 Instruction::new_with_bincode(
280 id(),
281 &UpgradeableLoaderInstruction::Upgrade,
282 vec![
283 AccountMeta::new(programdata_address, false),
284 AccountMeta::new(*program_address, false),
285 AccountMeta::new(*buffer_address, false),
286 AccountMeta::new(*spill_address, false),
287 AccountMeta::new_readonly(sysvar::rent::id(), false),
288 AccountMeta::new_readonly(sysvar::clock::id(), false),
289 AccountMeta::new_readonly(*authority_address, true),
290 ],
291 )
292}
293
294pub fn is_upgrade_instruction(instruction_data: &[u8]) -> bool {
295 !instruction_data.is_empty() && 3 == instruction_data[0]
296}
297
298pub fn is_set_authority_instruction(instruction_data: &[u8]) -> bool {
299 !instruction_data.is_empty() && 4 == instruction_data[0]
300}
301
302pub fn is_close_instruction(instruction_data: &[u8]) -> bool {
303 !instruction_data.is_empty() && 5 == instruction_data[0]
304}
305
306pub fn is_set_authority_checked_instruction(instruction_data: &[u8]) -> bool {
307 !instruction_data.is_empty() && 7 == instruction_data[0]
308}
309
310pub fn is_migrate_instruction(instruction_data: &[u8]) -> bool {
311 !instruction_data.is_empty() && 8 == instruction_data[0]
312}
313
314#[cfg(feature = "bincode")]
315pub fn set_buffer_authority(
317 buffer_address: &Pubkey,
318 current_authority_address: &Pubkey,
319 new_authority_address: &Pubkey,
320) -> Instruction {
321 Instruction::new_with_bincode(
322 id(),
323 &UpgradeableLoaderInstruction::SetAuthority,
324 vec![
325 AccountMeta::new(*buffer_address, false),
326 AccountMeta::new_readonly(*current_authority_address, true),
327 AccountMeta::new_readonly(*new_authority_address, false),
328 ],
329 )
330}
331
332#[cfg(feature = "bincode")]
333pub fn set_buffer_authority_checked(
336 buffer_address: &Pubkey,
337 current_authority_address: &Pubkey,
338 new_authority_address: &Pubkey,
339) -> Instruction {
340 Instruction::new_with_bincode(
341 id(),
342 &UpgradeableLoaderInstruction::SetAuthorityChecked,
343 vec![
344 AccountMeta::new(*buffer_address, false),
345 AccountMeta::new_readonly(*current_authority_address, true),
346 AccountMeta::new_readonly(*new_authority_address, true),
347 ],
348 )
349}
350
351#[cfg(feature = "bincode")]
352pub fn set_upgrade_authority(
354 program_address: &Pubkey,
355 current_authority_address: &Pubkey,
356 new_authority_address: Option<&Pubkey>,
357) -> Instruction {
358 let programdata_address = get_program_data_address(program_address);
359
360 let mut metas = vec![
361 AccountMeta::new(programdata_address, false),
362 AccountMeta::new_readonly(*current_authority_address, true),
363 ];
364 if let Some(address) = new_authority_address {
365 metas.push(AccountMeta::new_readonly(*address, false));
366 }
367 Instruction::new_with_bincode(id(), &UpgradeableLoaderInstruction::SetAuthority, metas)
368}
369
370#[cfg(feature = "bincode")]
371pub fn set_upgrade_authority_checked(
374 program_address: &Pubkey,
375 current_authority_address: &Pubkey,
376 new_authority_address: &Pubkey,
377) -> Instruction {
378 let programdata_address = get_program_data_address(program_address);
379
380 let metas = vec![
381 AccountMeta::new(programdata_address, false),
382 AccountMeta::new_readonly(*current_authority_address, true),
383 AccountMeta::new_readonly(*new_authority_address, true),
384 ];
385 Instruction::new_with_bincode(
386 id(),
387 &UpgradeableLoaderInstruction::SetAuthorityChecked,
388 metas,
389 )
390}
391
392#[cfg(feature = "bincode")]
393pub fn close(
395 close_address: &Pubkey,
396 recipient_address: &Pubkey,
397 authority_address: &Pubkey,
398) -> Instruction {
399 close_any(
400 close_address,
401 recipient_address,
402 Some(authority_address),
403 None,
404 )
405}
406
407#[cfg(feature = "bincode")]
408pub fn close_any(
410 close_address: &Pubkey,
411 recipient_address: &Pubkey,
412 authority_address: Option<&Pubkey>,
413 program_address: Option<&Pubkey>,
414) -> Instruction {
415 let mut metas = vec![
416 AccountMeta::new(*close_address, false),
417 AccountMeta::new(*recipient_address, false),
418 ];
419 if let Some(authority_address) = authority_address {
420 metas.push(AccountMeta::new_readonly(*authority_address, true));
421 }
422 if let Some(program_address) = program_address {
423 metas.push(AccountMeta::new(*program_address, false));
424 }
425 Instruction::new_with_bincode(id(), &UpgradeableLoaderInstruction::Close, metas)
426}
427
428#[cfg(feature = "bincode")]
429pub fn extend_program(
432 program_address: &Pubkey,
433 payer_address: Option<&Pubkey>,
434 additional_bytes: u32,
435) -> Instruction {
436 let program_data_address = get_program_data_address(program_address);
437 let mut metas = vec![
438 AccountMeta::new(program_data_address, false),
439 AccountMeta::new(*program_address, false),
440 ];
441 if let Some(payer_address) = payer_address {
442 metas.push(AccountMeta::new_readonly(
443 clone_solana_sdk_ids::system_program::id(),
444 false,
445 ));
446 metas.push(AccountMeta::new(*payer_address, true));
447 }
448 Instruction::new_with_bincode(
449 id(),
450 &UpgradeableLoaderInstruction::ExtendProgram { additional_bytes },
451 metas,
452 )
453}
454
455#[cfg(feature = "bincode")]
457pub fn migrate_program(
458 programdata_address: &Pubkey,
459 program_address: &Pubkey,
460 authority: &Pubkey,
461) -> Instruction {
462 let accounts = vec![
463 AccountMeta::new(*programdata_address, false),
464 AccountMeta::new(*program_address, false),
465 AccountMeta::new_readonly(*authority, true),
466 AccountMeta::new_readonly(loader_v4::id(), false),
467 ];
468
469 Instruction::new_with_bincode(id(), &UpgradeableLoaderInstruction::Migrate, accounts)
470}
471
472#[cfg(test)]
473mod tests {
474 use super::*;
475
476 fn assert_is_instruction<F>(
477 is_instruction_fn: F,
478 expected_instruction: UpgradeableLoaderInstruction,
479 ) where
480 F: Fn(&[u8]) -> bool,
481 {
482 let result = is_instruction_fn(
483 &bincode::serialize(&UpgradeableLoaderInstruction::InitializeBuffer).unwrap(),
484 );
485 let expected_result = matches!(
486 expected_instruction,
487 UpgradeableLoaderInstruction::InitializeBuffer
488 );
489 assert_eq!(expected_result, result);
490
491 let result = is_instruction_fn(
492 &bincode::serialize(&UpgradeableLoaderInstruction::Write {
493 offset: 0,
494 bytes: vec![],
495 })
496 .unwrap(),
497 );
498 let expected_result = matches!(
499 expected_instruction,
500 UpgradeableLoaderInstruction::Write {
501 offset: _,
502 bytes: _,
503 }
504 );
505 assert_eq!(expected_result, result);
506
507 let result = is_instruction_fn(
508 &bincode::serialize(&UpgradeableLoaderInstruction::DeployWithMaxDataLen {
509 max_data_len: 0,
510 })
511 .unwrap(),
512 );
513 let expected_result = matches!(
514 expected_instruction,
515 UpgradeableLoaderInstruction::DeployWithMaxDataLen { max_data_len: _ }
516 );
517 assert_eq!(expected_result, result);
518
519 let result =
520 is_instruction_fn(&bincode::serialize(&UpgradeableLoaderInstruction::Upgrade).unwrap());
521 let expected_result = matches!(expected_instruction, UpgradeableLoaderInstruction::Upgrade);
522 assert_eq!(expected_result, result);
523
524 let result = is_instruction_fn(
525 &bincode::serialize(&UpgradeableLoaderInstruction::SetAuthority).unwrap(),
526 );
527 let expected_result = matches!(
528 expected_instruction,
529 UpgradeableLoaderInstruction::SetAuthority
530 );
531 assert_eq!(expected_result, result);
532
533 let result =
534 is_instruction_fn(&bincode::serialize(&UpgradeableLoaderInstruction::Close).unwrap());
535 let expected_result = matches!(expected_instruction, UpgradeableLoaderInstruction::Close);
536 assert_eq!(expected_result, result);
537 }
538
539 #[test]
540 fn test_is_set_authority_instruction() {
541 assert!(!is_set_authority_instruction(&[]));
542 assert_is_instruction(
543 is_set_authority_instruction,
544 UpgradeableLoaderInstruction::SetAuthority {},
545 );
546 }
547
548 #[test]
549 fn test_is_set_authority_checked_instruction() {
550 assert!(!is_set_authority_checked_instruction(&[]));
551 assert_is_instruction(
552 is_set_authority_checked_instruction,
553 UpgradeableLoaderInstruction::SetAuthorityChecked {},
554 );
555 }
556
557 #[test]
558 fn test_is_upgrade_instruction() {
559 assert!(!is_upgrade_instruction(&[]));
560 assert_is_instruction(
561 is_upgrade_instruction,
562 UpgradeableLoaderInstruction::Upgrade {},
563 );
564 }
565
566 #[test]
567 fn test_is_migrate_instruction() {
568 assert!(!is_migrate_instruction(&[]));
569 assert_is_instruction(
570 is_migrate_instruction,
571 UpgradeableLoaderInstruction::Migrate {},
572 );
573 }
574}