Skip to content
This repository was archived by the owner on Mar 11, 2025. It is now read-only.

Commit 0d6832e

Browse files
stake-pool-cli: don't send UpdateValidatorListBalance transactions for subslices of validator list that have already been updated (#6059)
* remove unnecessary vote acc slice * nightly fmt * fix clippy * update stale validator_list_balance only if not force * merge * fmt * merge conflict * restore stale ixs, add fresh flag to update * fmt * fresh -> stale_only, update --force help
1 parent 8b22d75 commit 0d6832e

File tree

2 files changed

+138
-18
lines changed

2 files changed

+138
-18
lines changed

stake-pool/cli/src/main.rs

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,7 @@ fn command_vsa_add(
430430
}
431431

432432
if !config.no_update {
433-
command_update(config, stake_pool_address, false, false)?;
433+
command_update(config, stake_pool_address, false, false, false)?;
434434
}
435435

436436
// iterate until a free account is found
@@ -488,7 +488,7 @@ fn command_vsa_remove(
488488
vote_account: &Pubkey,
489489
) -> CommandResult {
490490
if !config.no_update {
491-
command_update(config, stake_pool_address, false, false)?;
491+
command_update(config, stake_pool_address, false, false, false)?;
492492
}
493493

494494
let stake_pool = get_stake_pool(&config.rpc_client, stake_pool_address)?;
@@ -535,7 +535,7 @@ fn command_increase_validator_stake(
535535
) -> CommandResult {
536536
let lamports = native_token::sol_to_lamports(amount);
537537
if !config.no_update {
538-
command_update(config, stake_pool_address, false, false)?;
538+
command_update(config, stake_pool_address, false, false, false)?;
539539
}
540540

541541
let stake_pool = get_stake_pool(&config.rpc_client, stake_pool_address)?;
@@ -574,7 +574,7 @@ fn command_decrease_validator_stake(
574574
) -> CommandResult {
575575
let lamports = native_token::sol_to_lamports(amount);
576576
if !config.no_update {
577-
command_update(config, stake_pool_address, false, false)?;
577+
command_update(config, stake_pool_address, false, false, false)?;
578578
}
579579

580580
let stake_pool = get_stake_pool(&config.rpc_client, stake_pool_address)?;
@@ -671,7 +671,7 @@ fn command_deposit_stake(
671671
referrer_token_account: &Option<Pubkey>,
672672
) -> CommandResult {
673673
if !config.no_update {
674-
command_update(config, stake_pool_address, false, false)?;
674+
command_update(config, stake_pool_address, false, false, false)?;
675675
}
676676

677677
let stake_pool = get_stake_pool(&config.rpc_client, stake_pool_address)?;
@@ -802,7 +802,7 @@ fn command_deposit_all_stake(
802802
referrer_token_account: &Option<Pubkey>,
803803
) -> CommandResult {
804804
if !config.no_update {
805-
command_update(config, stake_pool_address, false, false)?;
805+
command_update(config, stake_pool_address, false, false, false)?;
806806
}
807807

808808
let stake_addresses = get_all_stake(&config.rpc_client, stake_authority)?;
@@ -945,7 +945,7 @@ fn command_deposit_sol(
945945
amount: f64,
946946
) -> CommandResult {
947947
if !config.no_update {
948-
command_update(config, stake_pool_address, false, false)?;
948+
command_update(config, stake_pool_address, false, false, false)?;
949949
}
950950

951951
let amount = native_token::sol_to_lamports(amount);
@@ -1139,6 +1139,7 @@ fn command_update(
11391139
stake_pool_address: &Pubkey,
11401140
force: bool,
11411141
no_merge: bool,
1142+
stale_only: bool,
11421143
) -> CommandResult {
11431144
if config.no_update {
11441145
println!("Update requested, but --no-update flag specified, so doing nothing");
@@ -1158,14 +1159,24 @@ fn command_update(
11581159

11591160
let validator_list = get_validator_list(&config.rpc_client, &stake_pool.validator_list)?;
11601161

1161-
let (mut update_list_instructions, final_instructions) =
1162+
let (mut update_list_instructions, final_instructions) = if stale_only {
1163+
spl_stake_pool::instruction::update_stale_stake_pool(
1164+
&spl_stake_pool::id(),
1165+
&stake_pool,
1166+
&validator_list,
1167+
stake_pool_address,
1168+
no_merge,
1169+
epoch_info.epoch,
1170+
)
1171+
} else {
11621172
spl_stake_pool::instruction::update_stake_pool(
11631173
&spl_stake_pool::id(),
11641174
&stake_pool,
11651175
&validator_list,
11661176
stake_pool_address,
11671177
no_merge,
1168-
);
1178+
)
1179+
};
11691180

11701181
let update_list_instructions_len = update_list_instructions.len();
11711182
if update_list_instructions_len > 0 {
@@ -1360,7 +1371,7 @@ fn command_withdraw_stake(
13601371
pool_amount: f64,
13611372
) -> CommandResult {
13621373
if !config.no_update {
1363-
command_update(config, stake_pool_address, false, false)?;
1374+
command_update(config, stake_pool_address, false, false, false)?;
13641375
}
13651376

13661377
let stake_pool = get_stake_pool(&config.rpc_client, stake_pool_address)?;
@@ -1631,7 +1642,7 @@ fn command_withdraw_sol(
16311642
pool_amount: f64,
16321643
) -> CommandResult {
16331644
if !config.no_update {
1634-
command_update(config, stake_pool_address, false, false)?;
1645+
command_update(config, stake_pool_address, false, false, false)?;
16351646
}
16361647

16371648
let stake_pool = get_stake_pool(&config.rpc_client, stake_pool_address)?;
@@ -1748,7 +1759,7 @@ fn command_set_manager(
17481759
new_fee_receiver: &Option<Pubkey>,
17491760
) -> CommandResult {
17501761
if !config.no_update {
1751-
command_update(config, stake_pool_address, false, false)?;
1762+
command_update(config, stake_pool_address, false, false, false)?;
17521763
}
17531764
let stake_pool = get_stake_pool(&config.rpc_client, stake_pool_address)?;
17541765

@@ -1800,7 +1811,7 @@ fn command_set_staker(
18001811
new_staker: &Pubkey,
18011812
) -> CommandResult {
18021813
if !config.no_update {
1803-
command_update(config, stake_pool_address, false, false)?;
1814+
command_update(config, stake_pool_address, false, false, false)?;
18041815
}
18051816
let mut signers = vec![config.fee_payer.as_ref(), config.manager.as_ref()];
18061817
unique_signers!(signers);
@@ -1825,7 +1836,7 @@ fn command_set_funding_authority(
18251836
funding_type: FundingType,
18261837
) -> CommandResult {
18271838
if !config.no_update {
1828-
command_update(config, stake_pool_address, false, false)?;
1839+
command_update(config, stake_pool_address, false, false, false)?;
18291840
}
18301841
let mut signers = vec![config.fee_payer.as_ref(), config.manager.as_ref()];
18311842
unique_signers!(signers);
@@ -1850,7 +1861,7 @@ fn command_set_fee(
18501861
new_fee: FeeType,
18511862
) -> CommandResult {
18521863
if !config.no_update {
1853-
command_update(config, stake_pool_address, false, false)?;
1864+
command_update(config, stake_pool_address, false, false, false)?;
18541865
}
18551866
let mut signers = vec![config.fee_payer.as_ref(), config.manager.as_ref()];
18561867
unique_signers!(signers);
@@ -2418,14 +2429,20 @@ fn main() {
24182429
Arg::with_name("force")
24192430
.long("force")
24202431
.takes_value(false)
2421-
.help("Update all balances, even if it has already been performed this epoch."),
2432+
.help("Update balances, even if it has already been performed this epoch."),
24222433
)
24232434
.arg(
24242435
Arg::with_name("no_merge")
24252436
.long("no-merge")
24262437
.takes_value(false)
24272438
.help("Do not automatically merge transient stakes. Useful if the stake pool is in an expected state, but the balances still need to be updated."),
24282439
)
2440+
.arg(
2441+
Arg::with_name("stale_only")
2442+
.long("stale-only")
2443+
.takes_value(false)
2444+
.help("If set, only updates validator list balances that have not been updated for this epoch. Otherwise, updates all validator balances on the validator list."),
2445+
)
24292446
)
24302447
.subcommand(SubCommand::with_name("withdraw-stake")
24312448
.about("Withdraw active stake from the stake pool in exchange for pool tokens")
@@ -2903,7 +2920,8 @@ fn main() {
29032920
let stake_pool_address = pubkey_of(arg_matches, "pool").unwrap();
29042921
let no_merge = arg_matches.is_present("no_merge");
29052922
let force = arg_matches.is_present("force");
2906-
command_update(&config, &stake_pool_address, force, no_merge)
2923+
let stale_only = arg_matches.is_present("stale_only");
2924+
command_update(&config, &stake_pool_address, force, no_merge, stale_only)
29072925
}
29082926
("withdraw-stake", Some(arg_matches)) => {
29092927
let stake_pool_address = pubkey_of(arg_matches, "pool").unwrap();

stake-pool/program/src/instruction.rs

Lines changed: 103 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ use {
1616
instruction::{AccountMeta, Instruction},
1717
program_error::ProgramError,
1818
pubkey::Pubkey,
19-
stake, system_program, sysvar,
19+
stake,
20+
stake_history::Epoch,
21+
system_program, sysvar,
2022
},
2123
std::num::NonZeroU32,
2224
};
@@ -1496,6 +1498,47 @@ pub fn update_validator_list_balance_chunk(
14961498
})
14971499
}
14981500

1501+
/// Creates `UpdateValidatorListBalance` instruction (update validator stake
1502+
/// account balances)
1503+
///
1504+
/// Returns `None` if all validators in the given chunk has already been updated
1505+
/// for this epoch, returns the required instruction otherwise.
1506+
pub fn update_stale_validator_list_balance_chunk(
1507+
program_id: &Pubkey,
1508+
stake_pool: &Pubkey,
1509+
stake_pool_withdraw_authority: &Pubkey,
1510+
validator_list_address: &Pubkey,
1511+
reserve_stake: &Pubkey,
1512+
validator_list: &ValidatorList,
1513+
len: usize,
1514+
start_index: usize,
1515+
no_merge: bool,
1516+
current_epoch: Epoch,
1517+
) -> Result<Option<Instruction>, ProgramError> {
1518+
let validator_list_subslice = validator_list
1519+
.validators
1520+
.get(start_index..start_index.saturating_add(len))
1521+
.ok_or(ProgramError::InvalidInstructionData)?;
1522+
if validator_list_subslice.iter().all(|info| {
1523+
let last_update_epoch: u64 = info.last_update_epoch.into();
1524+
last_update_epoch >= current_epoch
1525+
}) {
1526+
return Ok(None);
1527+
}
1528+
update_validator_list_balance_chunk(
1529+
program_id,
1530+
stake_pool,
1531+
stake_pool_withdraw_authority,
1532+
validator_list_address,
1533+
reserve_stake,
1534+
validator_list,
1535+
len,
1536+
start_index,
1537+
no_merge,
1538+
)
1539+
.map(Some)
1540+
}
1541+
14991542
/// Creates `UpdateStakePoolBalance` instruction (pool balance from the stake
15001543
/// account list balances)
15011544
pub fn update_stake_pool_balance(
@@ -1599,6 +1642,65 @@ pub fn update_stake_pool(
15991642
(update_list_instructions, final_instructions)
16001643
}
16011644

1645+
/// Creates the `UpdateValidatorListBalance` instructions only for validators on
1646+
/// `validator_list` that have not been updated for this epoch, and the
1647+
/// `UpdateStakePoolBalance` instruction for fully updating the stake pool.
1648+
///
1649+
/// Basically same as [`update_stake_pool`], but skips validators that are
1650+
/// already updated for this epoch
1651+
pub fn update_stale_stake_pool(
1652+
program_id: &Pubkey,
1653+
stake_pool: &StakePool,
1654+
validator_list: &ValidatorList,
1655+
stake_pool_address: &Pubkey,
1656+
no_merge: bool,
1657+
current_epoch: Epoch,
1658+
) -> (Vec<Instruction>, Vec<Instruction>) {
1659+
let (withdraw_authority, _) =
1660+
find_withdraw_authority_program_address(program_id, stake_pool_address);
1661+
1662+
let update_list_instructions = validator_list
1663+
.validators
1664+
.chunks(MAX_VALIDATORS_TO_UPDATE)
1665+
.enumerate()
1666+
.filter_map(|(i, chunk)| {
1667+
// unwrap-safety: chunk len and offset are derived
1668+
update_stale_validator_list_balance_chunk(
1669+
program_id,
1670+
stake_pool_address,
1671+
&withdraw_authority,
1672+
&stake_pool.validator_list,
1673+
&stake_pool.reserve_stake,
1674+
validator_list,
1675+
chunk.len(),
1676+
i.saturating_mul(MAX_VALIDATORS_TO_UPDATE),
1677+
no_merge,
1678+
current_epoch,
1679+
)
1680+
.unwrap()
1681+
})
1682+
.collect();
1683+
1684+
let final_instructions = vec![
1685+
update_stake_pool_balance(
1686+
program_id,
1687+
stake_pool_address,
1688+
&withdraw_authority,
1689+
&stake_pool.validator_list,
1690+
&stake_pool.reserve_stake,
1691+
&stake_pool.manager_fee_account,
1692+
&stake_pool.pool_mint,
1693+
&stake_pool.token_program_id,
1694+
),
1695+
cleanup_removed_validator_entries(
1696+
program_id,
1697+
stake_pool_address,
1698+
&stake_pool.validator_list,
1699+
),
1700+
];
1701+
(update_list_instructions, final_instructions)
1702+
}
1703+
16021704
fn deposit_stake_internal(
16031705
program_id: &Pubkey,
16041706
stake_pool: &Pubkey,

0 commit comments

Comments
 (0)