|
31 | 31 | return_signers_with_config, CliProgram, CliProgramAccountType, CliProgramAuthority,
|
32 | 32 | CliProgramBuffer, CliProgramId, CliUpgradeableBuffer, CliUpgradeableBuffers,
|
33 | 33 | CliUpgradeableProgram, CliUpgradeableProgramClosed, CliUpgradeableProgramExtended,
|
34 |
| - CliUpgradeablePrograms, ReturnSignersConfig, |
| 34 | + CliUpgradeableProgramMigrated, CliUpgradeablePrograms, ReturnSignersConfig, |
35 | 35 | },
|
36 | 36 | solana_client::{
|
37 | 37 | connection_cache::ConnectionCache,
|
@@ -171,6 +171,10 @@ pub enum ProgramCliCommand {
|
171 | 171 | program_pubkey: Pubkey,
|
172 | 172 | additional_bytes: u32,
|
173 | 173 | },
|
| 174 | + Migrate { |
| 175 | + program_pubkey: Pubkey, |
| 176 | + authority_signer_index: SignerIndex, |
| 177 | + }, |
174 | 178 | }
|
175 | 179 |
|
176 | 180 | pub trait ProgramSubCommands {
|
@@ -632,6 +636,32 @@ impl ProgramSubCommands for App<'_, '_> {
|
632 | 636 | data account",
|
633 | 637 | ),
|
634 | 638 | ),
|
| 639 | + ) |
| 640 | + .subcommand( |
| 641 | + SubCommand::with_name("migrate") |
| 642 | + .about( |
| 643 | + "Migrates an upgradeable program to loader-v4", |
| 644 | + ) |
| 645 | + .arg( |
| 646 | + Arg::with_name("program_id") |
| 647 | + .index(1) |
| 648 | + .value_name("PROGRAM_ID") |
| 649 | + .takes_value(true) |
| 650 | + .required(true) |
| 651 | + .validator(is_valid_pubkey) |
| 652 | + .help("Address of the program to extend"), |
| 653 | + ) |
| 654 | + .arg( |
| 655 | + Arg::with_name("authority") |
| 656 | + .long("authority") |
| 657 | + .value_name("AUTHORITY_SIGNER") |
| 658 | + .takes_value(true) |
| 659 | + .validator(is_valid_signer) |
| 660 | + .help( |
| 661 | + "Upgrade authority [default: the default configured \ |
| 662 | + keypair]", |
| 663 | + ), |
| 664 | + ), |
635 | 665 | ),
|
636 | 666 | )
|
637 | 667 | .subcommand(
|
@@ -991,6 +1021,29 @@ pub fn parse_program_subcommand(
|
991 | 1021 | signers: signer_info.signers,
|
992 | 1022 | }
|
993 | 1023 | }
|
| 1024 | + ("migrate", Some(matches)) => { |
| 1025 | + let program_pubkey = pubkey_of(matches, "program_id").unwrap(); |
| 1026 | + |
| 1027 | + let (authority_signer, authority_pubkey) = |
| 1028 | + signer_of(matches, "authority", wallet_manager)?; |
| 1029 | + |
| 1030 | + let signer_info = default_signer.generate_unique_signers( |
| 1031 | + vec![ |
| 1032 | + Some(default_signer.signer_from_path(matches, wallet_manager)?), |
| 1033 | + authority_signer, |
| 1034 | + ], |
| 1035 | + matches, |
| 1036 | + wallet_manager, |
| 1037 | + )?; |
| 1038 | + |
| 1039 | + CliCommandInfo { |
| 1040 | + command: CliCommand::Program(ProgramCliCommand::Migrate { |
| 1041 | + program_pubkey, |
| 1042 | + authority_signer_index: signer_info.index_of(authority_pubkey).unwrap(), |
| 1043 | + }), |
| 1044 | + signers: signer_info.signers, |
| 1045 | + } |
| 1046 | + } |
994 | 1047 | _ => unreachable!(),
|
995 | 1048 | };
|
996 | 1049 | Ok(response)
|
@@ -1175,6 +1228,15 @@ pub fn process_program_subcommand(
|
1175 | 1228 | program_pubkey,
|
1176 | 1229 | additional_bytes,
|
1177 | 1230 | } => process_extend_program(&rpc_client, config, *program_pubkey, *additional_bytes),
|
| 1231 | + ProgramCliCommand::Migrate { |
| 1232 | + program_pubkey, |
| 1233 | + authority_signer_index, |
| 1234 | + } => process_migrate_program( |
| 1235 | + &rpc_client, |
| 1236 | + config, |
| 1237 | + *program_pubkey, |
| 1238 | + *authority_signer_index, |
| 1239 | + ), |
1178 | 1240 | }
|
1179 | 1241 | }
|
1180 | 1242 |
|
@@ -2387,6 +2449,97 @@ fn process_extend_program(
|
2387 | 2449 | }))
|
2388 | 2450 | }
|
2389 | 2451 |
|
| 2452 | +fn process_migrate_program( |
| 2453 | + rpc_client: &RpcClient, |
| 2454 | + config: &CliConfig, |
| 2455 | + program_pubkey: Pubkey, |
| 2456 | + authority_signer_index: SignerIndex, |
| 2457 | +) -> ProcessResult { |
| 2458 | + let payer_pubkey = config.signers[0].pubkey(); |
| 2459 | + let authority_signer = config.signers[authority_signer_index]; |
| 2460 | + |
| 2461 | + let program_account = match rpc_client |
| 2462 | + .get_account_with_commitment(&program_pubkey, config.commitment)? |
| 2463 | + .value |
| 2464 | + { |
| 2465 | + Some(program_account) => Ok(program_account), |
| 2466 | + None => Err(format!("Unable to find program {program_pubkey}")), |
| 2467 | + }?; |
| 2468 | + |
| 2469 | + if !bpf_loader_upgradeable::check_id(&program_account.owner) { |
| 2470 | + return Err(format!("Account {program_pubkey} is not an upgradeable program").into()); |
| 2471 | + } |
| 2472 | + |
| 2473 | + let programdata_pubkey = match program_account.state() { |
| 2474 | + Ok(UpgradeableLoaderState::Program { |
| 2475 | + programdata_address: programdata_pubkey, |
| 2476 | + }) => Ok(programdata_pubkey), |
| 2477 | + _ => Err(format!( |
| 2478 | + "Account {program_pubkey} is not an upgradeable program" |
| 2479 | + )), |
| 2480 | + }?; |
| 2481 | + |
| 2482 | + let programdata_account = match rpc_client |
| 2483 | + .get_account_with_commitment(&programdata_pubkey, config.commitment)? |
| 2484 | + .value |
| 2485 | + { |
| 2486 | + Some(programdata_account) => Ok(programdata_account), |
| 2487 | + None => Err(format!("Program {program_pubkey} is closed")), |
| 2488 | + }?; |
| 2489 | + |
| 2490 | + let upgrade_authority_address = match programdata_account.state() { |
| 2491 | + Ok(UpgradeableLoaderState::ProgramData { |
| 2492 | + slot: _, |
| 2493 | + upgrade_authority_address, |
| 2494 | + }) => Ok(upgrade_authority_address), |
| 2495 | + _ => Err(format!("Program {program_pubkey} is closed")), |
| 2496 | + }?; |
| 2497 | + |
| 2498 | + if upgrade_authority_address != Some(authority_signer.pubkey()) { |
| 2499 | + return Err(format!( |
| 2500 | + "Upgrade authority {:?} does not match {:?}", |
| 2501 | + upgrade_authority_address, |
| 2502 | + Some(authority_signer.pubkey()) |
| 2503 | + ) |
| 2504 | + .into()); |
| 2505 | + } |
| 2506 | + |
| 2507 | + let blockhash = rpc_client.get_latest_blockhash()?; |
| 2508 | + |
| 2509 | + let mut tx = Transaction::new_unsigned(Message::new( |
| 2510 | + &[bpf_loader_upgradeable::migrate_program( |
| 2511 | + &programdata_pubkey, |
| 2512 | + &program_pubkey, |
| 2513 | + &authority_signer.pubkey(), |
| 2514 | + )], |
| 2515 | + Some(&payer_pubkey), |
| 2516 | + )); |
| 2517 | + |
| 2518 | + tx.try_sign(&[config.signers[0]], blockhash)?; |
| 2519 | + let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config( |
| 2520 | + &tx, |
| 2521 | + config.commitment, |
| 2522 | + config.send_transaction_config, |
| 2523 | + ); |
| 2524 | + if let Err(err) = result { |
| 2525 | + if let ClientErrorKind::TransactionError(TransactionError::InstructionError( |
| 2526 | + _, |
| 2527 | + InstructionError::InvalidInstructionData, |
| 2528 | + )) = err.kind() |
| 2529 | + { |
| 2530 | + return Err("Migrating a program is not supported by the cluster".into()); |
| 2531 | + } else { |
| 2532 | + return Err(format!("Migrate program failed: {err}").into()); |
| 2533 | + } |
| 2534 | + } |
| 2535 | + |
| 2536 | + Ok(config |
| 2537 | + .output_format |
| 2538 | + .formatted_string(&CliUpgradeableProgramMigrated { |
| 2539 | + program_id: program_pubkey.to_string(), |
| 2540 | + })) |
| 2541 | +} |
| 2542 | + |
2390 | 2543 | pub fn calculate_max_chunk_size<F>(create_msg: &F) -> usize
|
2391 | 2544 | where
|
2392 | 2545 | F: Fn(u32, Vec<u8>) -> Message,
|
|
0 commit comments