Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add StakeInstruction::Redelegate #26294

Merged
merged 2 commits into from
Jul 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ pub enum CliCommand {
nonce_authority: SignerIndex,
memo: Option<String>,
fee_payer: SignerIndex,
redelegation_stake_account_pubkey: Option<Pubkey>,
},
SplitStake {
stake_account_pubkey: Pubkey,
Expand Down Expand Up @@ -683,6 +684,9 @@ pub fn parse_command(
("delegate-stake", Some(matches)) => {
parse_stake_delegate_stake(matches, default_signer, wallet_manager)
}
("redelegate-stake", Some(matches)) => {
parse_stake_delegate_stake(matches, default_signer, wallet_manager)
}
("withdraw-stake", Some(matches)) => {
parse_stake_withdraw_stake(matches, default_signer, wallet_manager)
}
Expand Down Expand Up @@ -1136,6 +1140,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
nonce_authority,
memo,
fee_payer,
redelegation_stake_account_pubkey,
} => process_delegate_stake(
&rpc_client,
config,
Expand All @@ -1150,6 +1155,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
*nonce_authority,
memo.as_ref(),
*fee_payer,
redelegation_stake_account_pubkey.as_ref(),
),
CliCommand::SplitStake {
stake_account_pubkey,
Expand Down
140 changes: 134 additions & 6 deletions cli/src/stake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,51 @@ impl StakeSubCommands for App<'_, '_> {
.arg(fee_payer_arg())
.arg(memo_arg())
)
.subcommand(
SubCommand::with_name("redelegate-stake")
.about("Redelegate active stake to another vote account")
.arg(
Arg::with_name("force")
.long("force")
.takes_value(false)
.hidden(true) // Don't document this argument to discourage its use
.help("Override vote account sanity checks (use carefully!)")
)
.arg(
pubkey!(Arg::with_name("stake_account_pubkey")
.index(1)
.value_name("STAKE_ACCOUNT_ADDRESS")
.required(true),
"Existing delegated stake account that has been fully activated. \
On success this stake account will be scheduled for deactivation and the rent-exempt balance \
may be withdrawn once fully deactivated")
)
.arg(
pubkey!(Arg::with_name("vote_account_pubkey")
.index(2)
.value_name("REDELEGATED_VOTE_ACCOUNT_ADDRESS")
.required(true),
"The vote account to which the stake will be redelegated")
)
.arg(
Arg::with_name("redelegation_stake_account")
.index(3)
.value_name("REDELEGATION_STAKE_ACCOUNT")
.takes_value(true)
.required(true)
.validator(is_valid_signer)
.help("Stake account to create for the redelegation. \
On success this stake account will be created and scheduled for activation with all \
the stake in the existing stake account, exclusive of the rent-exempt balance retained \
in the existing account")
)
.arg(stake_authority_arg())
.offline_args()
.nonce_args(false)
.arg(fee_payer_arg())
.arg(memo_arg())
)

.subcommand(
SubCommand::with_name("stake-authorize")
.about("Authorize a new signing keypair for the given stake account")
Expand Down Expand Up @@ -753,6 +798,8 @@ pub fn parse_stake_delegate_stake(
pubkey_of_signer(matches, "stake_account_pubkey", wallet_manager)?.unwrap();
let vote_account_pubkey =
pubkey_of_signer(matches, "vote_account_pubkey", wallet_manager)?.unwrap();
let (redelegation_stake_account, redelegation_stake_account_pubkey) =
signer_of(matches, "redelegation_stake_account", wallet_manager)?;
let force = matches.is_present("force");
let sign_only = matches.is_present(SIGN_ONLY_ARG.name);
let dump_transaction_message = matches.is_present(DUMP_TRANSACTION_MESSAGE.name);
Expand All @@ -765,7 +812,7 @@ pub fn parse_stake_delegate_stake(
signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
let (fee_payer, fee_payer_pubkey) = signer_of(matches, FEE_PAYER_ARG.name, wallet_manager)?;

let mut bulk_signers = vec![stake_authority, fee_payer];
let mut bulk_signers = vec![stake_authority, fee_payer, redelegation_stake_account];
if nonce_account.is_some() {
bulk_signers.push(nonce_authority);
}
Expand All @@ -785,6 +832,7 @@ pub fn parse_stake_delegate_stake(
nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(),
memo,
fee_payer: signer_info.index_of(fee_payer_pubkey).unwrap(),
redelegation_stake_account_pubkey,
},
signers: signer_info.signers,
})
Expand Down Expand Up @@ -2414,11 +2462,28 @@ pub fn process_delegate_stake(
nonce_authority: SignerIndex,
memo: Option<&String>,
fee_payer: SignerIndex,
redelegation_stake_account_pubkey: Option<&Pubkey>,
) -> ProcessResult {
check_unique_pubkeys(
(&config.signers[0].pubkey(), "cli keypair".to_string()),
(stake_account_pubkey, "stake_account_pubkey".to_string()),
)?;
if let Some(redelegation_stake_account_pubkey) = &redelegation_stake_account_pubkey {
check_unique_pubkeys(
(stake_account_pubkey, "stake_account_pubkey".to_string()),
mvines marked this conversation as resolved.
Show resolved Hide resolved
(
redelegation_stake_account_pubkey,
"redelegation_stake_account".to_string(),
),
)?;
check_unique_pubkeys(
(&config.signers[0].pubkey(), "cli keypair".to_string()),
(
redelegation_stake_account_pubkey,
"redelegation_stake_account".to_string(),
),
)?;
}
let stake_authority = config.signers[stake_authority];

if !sign_only {
Expand Down Expand Up @@ -2471,12 +2536,22 @@ pub fn process_delegate_stake(

let recent_blockhash = blockhash_query.get_blockhash(rpc_client, config.commitment)?;

let ixs = vec![stake_instruction::delegate_stake(
stake_account_pubkey,
&stake_authority.pubkey(),
vote_account_pubkey,
)]
let ixs = if let Some(redelegation_stake_account_pubkey) = &redelegation_stake_account_pubkey {
stake_instruction::redelegate(
stake_account_pubkey,
&stake_authority.pubkey(),
vote_account_pubkey,
redelegation_stake_account_pubkey,
)
} else {
vec![stake_instruction::delegate_stake(
stake_account_pubkey,
&stake_authority.pubkey(),
vote_account_pubkey,
)]
}
.with_memo(memo);

let nonce_authority = config.signers[nonce_authority];
let fee_payer = config.signers[fee_payer];

Expand Down Expand Up @@ -3867,6 +3942,7 @@ mod tests {
nonce_authority: 0,
memo: None,
fee_payer: 0,
redelegation_stake_account_pubkey: None,
},
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
}
Expand Down Expand Up @@ -3898,6 +3974,7 @@ mod tests {
nonce_authority: 0,
memo: None,
fee_payer: 0,
redelegation_stake_account_pubkey: None,
},
signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
Expand Down Expand Up @@ -3931,6 +4008,7 @@ mod tests {
nonce_authority: 0,
memo: None,
fee_payer: 0,
redelegation_stake_account_pubkey: None,
},
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
}
Expand Down Expand Up @@ -3965,6 +4043,7 @@ mod tests {
nonce_authority: 0,
memo: None,
fee_payer: 0,
redelegation_stake_account_pubkey: None,
},
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
}
Expand Down Expand Up @@ -3994,6 +4073,7 @@ mod tests {
nonce_authority: 0,
memo: None,
fee_payer: 0,
redelegation_stake_account_pubkey: None,
},
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
}
Expand Down Expand Up @@ -4033,6 +4113,7 @@ mod tests {
nonce_authority: 0,
memo: None,
fee_payer: 1,
redelegation_stake_account_pubkey: None,
},
signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
Expand Down Expand Up @@ -4081,6 +4162,7 @@ mod tests {
nonce_authority: 2,
memo: None,
fee_payer: 1,
redelegation_stake_account_pubkey: None,
},
signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
Expand Down Expand Up @@ -4117,6 +4199,7 @@ mod tests {
nonce_authority: 0,
memo: None,
fee_payer: 1,
redelegation_stake_account_pubkey: None,
},
signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
Expand All @@ -4125,6 +4208,51 @@ mod tests {
}
);

// Test RedelegateStake Subcommand (minimal test due to the significant implementation
// overlap with DelegateStake)
let (redelegation_stake_account_keypair_file, mut redelegation_stake_account_tmp_file) =
make_tmp_file();
let redelegation_stake_account_keypair = Keypair::new();
write_keypair(
&redelegation_stake_account_keypair,
redelegation_stake_account_tmp_file.as_file_mut(),
)
.unwrap();
let redelegation_stake_account_pubkey = redelegation_stake_account_keypair.pubkey();

let test_redelegate_stake = test_commands.clone().get_matches_from(vec![
"test",
"redelegate-stake",
&stake_account_string,
&vote_account_string,
&redelegation_stake_account_keypair_file,
]);
assert_eq!(
parse_command(&test_redelegate_stake, &default_signer, &mut None).unwrap(),
CliCommandInfo {
command: CliCommand::DelegateStake {
stake_account_pubkey,
vote_account_pubkey,
stake_authority: 0,
force: false,
sign_only: false,
dump_transaction_message: false,
blockhash_query: BlockhashQuery::default(),
nonce_account: None,
nonce_authority: 0,
memo: None,
fee_payer: 0,
redelegation_stake_account_pubkey: Some(redelegation_stake_account_pubkey),
},
signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
read_keypair_file(&redelegation_stake_account_keypair_file)
.unwrap()
.into()
],
}
);

// Test WithdrawStake Subcommand
let test_withdraw_stake = test_commands.clone().get_matches_from(vec![
"test",
Expand Down
18 changes: 17 additions & 1 deletion cli/src/test_utils.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use {
solana_client::rpc_client::RpcClient,
solana_sdk::{clock::DEFAULT_MS_PER_SLOT, commitment_config::CommitmentConfig},
solana_sdk::{
clock::{Epoch, DEFAULT_MS_PER_SLOT},
commitment_config::CommitmentConfig,
},
std::{thread::sleep, time::Duration},
};

Expand Down Expand Up @@ -35,3 +38,16 @@ pub fn check_ready(rpc_client: &RpcClient) {
sleep(Duration::from_millis(DEFAULT_MS_PER_SLOT));
}
}

pub fn wait_for_next_epoch(rpc_client: &RpcClient) -> Epoch {
let current_epoch = rpc_client.get_epoch_info().unwrap().epoch;
println!("waiting for epoch {}", current_epoch + 1);
loop {
sleep(Duration::from_millis(DEFAULT_MS_PER_SLOT));

let next_epoch = rpc_client.get_epoch_info().unwrap().epoch;
if next_epoch > current_epoch {
return next_epoch;
}
}
}
Loading