Skip to content

Commit ae58ed5

Browse files
committed
Adds CLI command.
1 parent fa48d3f commit ae58ed5

File tree

2 files changed

+173
-1
lines changed

2 files changed

+173
-1
lines changed

cli-output/src/cli_output.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2468,6 +2468,25 @@ impl fmt::Display for CliUpgradeableProgramExtended {
24682468
}
24692469
}
24702470

2471+
#[derive(Serialize, Deserialize)]
2472+
#[serde(rename_all = "camelCase")]
2473+
pub struct CliUpgradeableProgramMigrated {
2474+
pub program_id: String,
2475+
}
2476+
impl QuietDisplay for CliUpgradeableProgramMigrated {}
2477+
impl VerboseDisplay for CliUpgradeableProgramMigrated {}
2478+
impl fmt::Display for CliUpgradeableProgramMigrated {
2479+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2480+
writeln!(f)?;
2481+
writeln!(
2482+
f,
2483+
"Migrated Program Id {} from loader-v3 to loader-v4",
2484+
&self.program_id,
2485+
)?;
2486+
Ok(())
2487+
}
2488+
}
2489+
24712490
#[derive(Clone, Serialize, Deserialize)]
24722491
#[serde(rename_all = "camelCase")]
24732492
pub struct CliUpgradeableBuffer {

cli/src/program.rs

Lines changed: 154 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use {
3131
return_signers_with_config, CliProgram, CliProgramAccountType, CliProgramAuthority,
3232
CliProgramBuffer, CliProgramId, CliUpgradeableBuffer, CliUpgradeableBuffers,
3333
CliUpgradeableProgram, CliUpgradeableProgramClosed, CliUpgradeableProgramExtended,
34-
CliUpgradeablePrograms, ReturnSignersConfig,
34+
CliUpgradeableProgramMigrated, CliUpgradeablePrograms, ReturnSignersConfig,
3535
},
3636
solana_client::{
3737
connection_cache::ConnectionCache,
@@ -171,6 +171,10 @@ pub enum ProgramCliCommand {
171171
program_pubkey: Pubkey,
172172
additional_bytes: u32,
173173
},
174+
Migrate {
175+
program_pubkey: Pubkey,
176+
authority_signer_index: SignerIndex,
177+
},
174178
}
175179

176180
pub trait ProgramSubCommands {
@@ -632,6 +636,32 @@ impl ProgramSubCommands for App<'_, '_> {
632636
data account",
633637
),
634638
),
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+
),
635665
),
636666
)
637667
.subcommand(
@@ -991,6 +1021,29 @@ pub fn parse_program_subcommand(
9911021
signers: signer_info.signers,
9921022
}
9931023
}
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+
}
9941047
_ => unreachable!(),
9951048
};
9961049
Ok(response)
@@ -1175,6 +1228,15 @@ pub fn process_program_subcommand(
11751228
program_pubkey,
11761229
additional_bytes,
11771230
} => 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+
),
11781240
}
11791241
}
11801242

@@ -2387,6 +2449,97 @@ fn process_extend_program(
23872449
}))
23882450
}
23892451

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+
23902543
pub fn calculate_max_chunk_size<F>(create_msg: &F) -> usize
23912544
where
23922545
F: Fn(u32, Vec<u8>) -> Message,

0 commit comments

Comments
 (0)