Skip to content

Commit 08c4cb5

Browse files
authored
stake-pool-cli: Add increase / decrease validator stake (solana-labs#1619)
1 parent fdba057 commit 08c4cb5

File tree

5 files changed

+183
-39
lines changed

5 files changed

+183
-39
lines changed

stake-pool/cli/src/main.rs

+162-14
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,95 @@ fn command_vsa_remove(
395395
Ok(())
396396
}
397397

398+
fn command_increase_validator_stake(
399+
config: &Config,
400+
stake_pool_address: &Pubkey,
401+
vote_account: &Pubkey,
402+
lamports: u64,
403+
) -> CommandResult {
404+
if !config.no_update {
405+
command_update(config, stake_pool_address)?;
406+
}
407+
408+
let stake_pool = get_stake_pool(&config.rpc_client, stake_pool_address)?;
409+
let pool_withdraw_authority =
410+
find_withdraw_authority_program_address(&spl_stake_pool::id(), stake_pool_address).0;
411+
let (transient_stake_address, _) = find_transient_stake_program_address(
412+
&spl_stake_pool::id(),
413+
&vote_account,
414+
stake_pool_address,
415+
);
416+
417+
let mut transaction = Transaction::new_with_payer(
418+
&[spl_stake_pool::instruction::increase_validator_stake(
419+
&spl_stake_pool::id(),
420+
&stake_pool_address,
421+
&config.staker.pubkey(),
422+
&pool_withdraw_authority,
423+
&stake_pool.validator_list,
424+
&stake_pool.reserve_stake,
425+
&transient_stake_address,
426+
&vote_account,
427+
lamports,
428+
)],
429+
Some(&config.fee_payer.pubkey()),
430+
);
431+
432+
let (recent_blockhash, fee_calculator) = config.rpc_client.get_recent_blockhash()?;
433+
check_fee_payer_balance(config, fee_calculator.calculate_fee(&transaction.message()))?;
434+
transaction.sign(
435+
&[config.fee_payer.as_ref(), config.staker.as_ref()],
436+
recent_blockhash,
437+
);
438+
send_transaction(&config, transaction)?;
439+
Ok(())
440+
}
441+
442+
fn command_decrease_validator_stake(
443+
config: &Config,
444+
stake_pool_address: &Pubkey,
445+
vote_account: &Pubkey,
446+
lamports: u64,
447+
) -> CommandResult {
448+
if !config.no_update {
449+
command_update(config, stake_pool_address)?;
450+
}
451+
452+
let stake_pool = get_stake_pool(&config.rpc_client, stake_pool_address)?;
453+
let pool_withdraw_authority =
454+
find_withdraw_authority_program_address(&spl_stake_pool::id(), stake_pool_address).0;
455+
let (validator_stake_address, _) =
456+
find_stake_program_address(&spl_stake_pool::id(), &vote_account, stake_pool_address);
457+
let (transient_stake_address, _) = find_transient_stake_program_address(
458+
&spl_stake_pool::id(),
459+
&vote_account,
460+
stake_pool_address,
461+
);
462+
463+
let mut transaction = Transaction::new_with_payer(
464+
&[spl_stake_pool::instruction::decrease_validator_stake(
465+
&spl_stake_pool::id(),
466+
&stake_pool_address,
467+
&config.staker.pubkey(),
468+
&pool_withdraw_authority,
469+
&stake_pool.validator_list,
470+
&validator_stake_address,
471+
&transient_stake_address,
472+
lamports,
473+
)],
474+
Some(&config.fee_payer.pubkey()),
475+
);
476+
477+
let (recent_blockhash, fee_calculator) = config.rpc_client.get_recent_blockhash()?;
478+
check_fee_payer_balance(config, fee_calculator.calculate_fee(&transaction.message()))?;
479+
transaction.sign(
480+
&[config.fee_payer.as_ref(), config.staker.as_ref()],
481+
recent_blockhash,
482+
);
483+
send_transaction(&config, transaction)?;
484+
Ok(())
485+
}
486+
398487
fn unwrap_create_token_account<F>(
399488
config: &Config,
400489
token_optional: &Option<Pubkey>,
@@ -630,21 +719,10 @@ fn command_update(config: &Config, stake_pool_address: &Pubkey) -> CommandResult
630719

631720
let validator_list = get_validator_list(&config.rpc_client, &stake_pool.validator_list)?;
632721

633-
let accounts_to_update: Vec<Pubkey> = validator_list
722+
let vote_accounts: Vec<Pubkey> = validator_list
634723
.validators
635724
.iter()
636-
.filter_map(|item| {
637-
if item.last_update_epoch >= epoch_info.epoch {
638-
None
639-
} else {
640-
let (stake_account, _) = find_stake_program_address(
641-
&spl_stake_pool::id(),
642-
&item.vote_account_address,
643-
&stake_pool_address,
644-
);
645-
Some(stake_account)
646-
}
647-
})
725+
.map(|item| item.vote_account_address)
648726
.collect();
649727

650728
println!("Updating stake pool...");
@@ -653,7 +731,7 @@ fn command_update(config: &Config, stake_pool_address: &Pubkey) -> CommandResult
653731

654732
let mut instructions: Vec<Instruction> = vec![];
655733
let mut start_index = 0;
656-
for accounts_chunk in accounts_to_update.chunks(MAX_VALIDATORS_TO_UPDATE) {
734+
for accounts_chunk in vote_accounts.chunks(MAX_VALIDATORS_TO_UPDATE) {
657735
instructions.push(spl_stake_pool::instruction::update_validator_list_balance(
658736
&spl_stake_pool::id(),
659737
stake_pool_address,
@@ -1181,6 +1259,64 @@ fn main() {
11811259
Defaults to the wallet owner pubkey."),
11821260
)
11831261
)
1262+
.subcommand(SubCommand::with_name("increase-validator-stake")
1263+
.about("Increase stake to a validator, drawing from the stake pool reserve. Must be signed by the pool staker.")
1264+
.arg(
1265+
Arg::with_name("pool")
1266+
.index(1)
1267+
.validator(is_pubkey)
1268+
.value_name("POOL_ADDRESS")
1269+
.takes_value(true)
1270+
.required(true)
1271+
.help("Stake pool address"),
1272+
)
1273+
.arg(
1274+
Arg::with_name("vote_account")
1275+
.index(2)
1276+
.validator(is_pubkey)
1277+
.value_name("VOTE_ACCOUNT_ADDRESS")
1278+
.takes_value(true)
1279+
.required(true)
1280+
.help("Vote account for the validator to increase stake to"),
1281+
)
1282+
.arg(
1283+
Arg::with_name("lamports")
1284+
.index(3)
1285+
.validator(is_pubkey)
1286+
.value_name("LAMPORTS")
1287+
.takes_value(true)
1288+
.help("Amount in lamports to add to the validator stake account. Must be at least the rent-exempt amount for a stake plus 1 SOL for merging."),
1289+
)
1290+
)
1291+
.subcommand(SubCommand::with_name("decrease-validator-stake")
1292+
.about("Decrease stake to a validator, splitting from the active stake. Must be signed by the pool staker.")
1293+
.arg(
1294+
Arg::with_name("pool")
1295+
.index(1)
1296+
.validator(is_pubkey)
1297+
.value_name("POOL_ADDRESS")
1298+
.takes_value(true)
1299+
.required(true)
1300+
.help("Stake pool address"),
1301+
)
1302+
.arg(
1303+
Arg::with_name("vote_account")
1304+
.index(2)
1305+
.validator(is_pubkey)
1306+
.value_name("VOTE_ACCOUNT_ADDRESS")
1307+
.takes_value(true)
1308+
.required(true)
1309+
.help("Vote account for the validator to decrease stake from"),
1310+
)
1311+
.arg(
1312+
Arg::with_name("lamports")
1313+
.index(3)
1314+
.validator(is_pubkey)
1315+
.value_name("LAMPORTS")
1316+
.takes_value(true)
1317+
.help("Amount in lamports to remove from the validator stake account. Must be at least the rent-exempt amount for a stake."),
1318+
)
1319+
)
11841320
.subcommand(SubCommand::with_name("deposit")
11851321
.about("Add stake account to the stake pool")
11861322
.arg(
@@ -1455,6 +1591,18 @@ fn main() {
14551591
let new_authority: Option<Pubkey> = pubkey_of(arg_matches, "new_authority");
14561592
command_vsa_remove(&config, &stake_pool_address, &vote_account, &new_authority)
14571593
}
1594+
("increase-validator-stake", Some(arg_matches)) => {
1595+
let stake_pool_address = pubkey_of(arg_matches, "pool").unwrap();
1596+
let vote_account = pubkey_of(arg_matches, "vote_account").unwrap();
1597+
let lamports = value_t_or_exit!(arg_matches, "lamports", u64);
1598+
command_increase_validator_stake(&config, &stake_pool_address, &vote_account, lamports)
1599+
}
1600+
("decrease-validator-stake", Some(arg_matches)) => {
1601+
let stake_pool_address = pubkey_of(arg_matches, "pool").unwrap();
1602+
let vote_account = pubkey_of(arg_matches, "vote_account").unwrap();
1603+
let lamports = value_t_or_exit!(arg_matches, "lamports", u64);
1604+
command_decrease_validator_stake(&config, &stake_pool_address, &vote_account, lamports)
1605+
}
14581606
("deposit", Some(arg_matches)) => {
14591607
let stake_pool_address = pubkey_of(arg_matches, "pool").unwrap();
14601608
let stake_account = pubkey_of(arg_matches, "stake_account").unwrap();

stake-pool/program/src/instruction.rs

+12-8
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,7 @@ pub fn decrease_validator_stake(
391391
validator_stake: &Pubkey,
392392
transient_stake: &Pubkey,
393393
lamports: u64,
394-
) -> Result<Instruction, ProgramError> {
394+
) -> Instruction {
395395
let accounts = vec![
396396
AccountMeta::new_readonly(*stake_pool, false),
397397
AccountMeta::new_readonly(*staker, true),
@@ -404,11 +404,13 @@ pub fn decrease_validator_stake(
404404
AccountMeta::new_readonly(system_program::id(), false),
405405
AccountMeta::new_readonly(stake_program::id(), false),
406406
];
407-
Ok(Instruction {
407+
Instruction {
408408
program_id: *program_id,
409409
accounts,
410-
data: StakePoolInstruction::DecreaseValidatorStake(lamports).try_to_vec()?,
411-
})
410+
data: StakePoolInstruction::DecreaseValidatorStake(lamports)
411+
.try_to_vec()
412+
.unwrap(),
413+
}
412414
}
413415

414416
/// Creates `IncreaseValidatorStake` instruction (rebalance from reserve account to
@@ -423,7 +425,7 @@ pub fn increase_validator_stake(
423425
transient_stake: &Pubkey,
424426
validator: &Pubkey,
425427
lamports: u64,
426-
) -> Result<Instruction, ProgramError> {
428+
) -> Instruction {
427429
let accounts = vec![
428430
AccountMeta::new_readonly(*stake_pool, false),
429431
AccountMeta::new_readonly(*staker, true),
@@ -439,11 +441,13 @@ pub fn increase_validator_stake(
439441
AccountMeta::new_readonly(system_program::id(), false),
440442
AccountMeta::new_readonly(stake_program::id(), false),
441443
];
442-
Ok(Instruction {
444+
Instruction {
443445
program_id: *program_id,
444446
accounts,
445-
data: StakePoolInstruction::IncreaseValidatorStake(lamports).try_to_vec()?,
446-
})
447+
data: StakePoolInstruction::IncreaseValidatorStake(lamports)
448+
.try_to_vec()
449+
.unwrap(),
450+
}
447451
}
448452

449453
/// Creates `UpdateValidatorListBalance` instruction (update validator stake account balances)

stake-pool/program/tests/decrease.rs

+3-6
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,7 @@ async fn fail_with_wrong_withdraw_authority() {
155155
&validator_stake.stake_account,
156156
&validator_stake.transient_stake_account,
157157
decrease_lamports,
158-
)
159-
.unwrap()],
158+
)],
160159
Some(&payer.pubkey()),
161160
&[&payer, &stake_pool_accounts.staker],
162161
recent_blockhash,
@@ -201,8 +200,7 @@ async fn fail_with_wrong_validator_list() {
201200
&validator_stake.stake_account,
202201
&validator_stake.transient_stake_account,
203202
decrease_lamports,
204-
)
205-
.unwrap()],
203+
)],
206204
Some(&payer.pubkey()),
207205
&[&payer, &stake_pool_accounts.staker],
208206
recent_blockhash,
@@ -258,8 +256,7 @@ async fn fail_with_unknown_validator() {
258256
&unknown_stake.stake_account,
259257
&unknown_stake.transient_stake_account,
260258
decrease_lamports,
261-
)
262-
.unwrap()],
259+
)],
263260
Some(&payer.pubkey()),
264261
&[&payer, &stake_pool_accounts.staker],
265262
recent_blockhash,

stake-pool/program/tests/helpers/mod.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -831,8 +831,7 @@ impl StakePoolAccounts {
831831
validator_stake,
832832
transient_stake,
833833
lamports,
834-
)
835-
.unwrap()],
834+
)],
836835
Some(&payer.pubkey()),
837836
&[payer, &self.staker],
838837
*recent_blockhash,
@@ -860,8 +859,7 @@ impl StakePoolAccounts {
860859
transient_stake,
861860
validator,
862861
lamports,
863-
)
864-
.unwrap()],
862+
)],
865863
Some(&payer.pubkey()),
866864
&[payer, &self.staker],
867865
*recent_blockhash,

stake-pool/program/tests/increase.rs

+4-7
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ async fn setup() -> (
2727
) {
2828
let (mut banks_client, payer, recent_blockhash) = program_test().start().await;
2929
let stake_pool_accounts = StakePoolAccounts::new();
30-
let reserve_lamports = 100_000_000;
30+
let reserve_lamports = 100_000_000_000;
3131
stake_pool_accounts
3232
.initialize_stake_pool(
3333
&mut banks_client,
@@ -156,8 +156,7 @@ async fn fail_with_wrong_withdraw_authority() {
156156
&validator_stake.transient_stake_account,
157157
&validator_stake.vote.pubkey(),
158158
reserve_lamports / 2,
159-
)
160-
.unwrap()],
159+
)],
161160
Some(&payer.pubkey()),
162161
&[&payer, &stake_pool_accounts.staker],
163162
recent_blockhash,
@@ -202,8 +201,7 @@ async fn fail_with_wrong_validator_list() {
202201
&validator_stake.transient_stake_account,
203202
&validator_stake.vote.pubkey(),
204203
reserve_lamports / 2,
205-
)
206-
.unwrap()],
204+
)],
207205
Some(&payer.pubkey()),
208206
&[&payer, &stake_pool_accounts.staker],
209207
recent_blockhash,
@@ -259,8 +257,7 @@ async fn fail_with_unknown_validator() {
259257
&unknown_stake.transient_stake_account,
260258
&unknown_stake.vote.pubkey(),
261259
reserve_lamports / 2,
262-
)
263-
.unwrap()],
260+
)],
264261
Some(&payer.pubkey()),
265262
&[&payer, &stake_pool_accounts.staker],
266263
recent_blockhash,

0 commit comments

Comments
 (0)