From 6a1d87c569cf56fb82851be020feb3ad9d2f3595 Mon Sep 17 00:00:00 2001 From: billythedummy Date: Thu, 4 Jan 2024 12:42:17 +0800 Subject: [PATCH 01/10] remove unnecessary vote acc slice --- stake-pool/program/src/instruction.rs | 110 ++++++++++-------- stake-pool/program/tests/decrease.rs | 1 - stake-pool/program/tests/deposit.rs | 1 - .../program/tests/deposit_edge_cases.rs | 1 - stake-pool/program/tests/force_destake.rs | 3 - stake-pool/program/tests/helpers/mod.rs | 26 +++-- stake-pool/program/tests/huge_pool.rs | 14 +-- stake-pool/program/tests/increase.rs | 1 - stake-pool/program/tests/redelegate.rs | 20 ---- stake-pool/program/tests/set_epoch_fee.rs | 2 - .../program/tests/set_withdrawal_fee.rs | 7 -- .../tests/update_stake_pool_balance.rs | 14 +-- .../tests/update_validator_list_balance.rs | 65 +---------- .../update_validator_list_balance_hijack.rs | 24 +--- stake-pool/program/tests/vsa_remove.rs | 8 +- .../program/tests/withdraw_edge_cases.rs | 10 -- 16 files changed, 95 insertions(+), 212 deletions(-) diff --git a/stake-pool/program/src/instruction.rs b/stake-pool/program/src/instruction.rs index 8bc41e878c6..b4441caacd0 100644 --- a/stake-pool/program/src/instruction.rs +++ b/stake-pool/program/src/instruction.rs @@ -2,6 +2,8 @@ #![allow(clippy::too_many_arguments)] +use crate::state::ValidatorStakeInfo; + use { crate::{ find_deposit_authority_program_address, find_ephemeral_stake_program_address, @@ -1370,8 +1372,8 @@ pub fn update_validator_list_balance( validator_list_address: &Pubkey, reserve_stake: &Pubkey, validator_list: &ValidatorList, - validator_vote_accounts: &[Pubkey], - start_index: u32, + len: usize, + start_index: usize, no_merge: bool, ) -> Instruction { let mut accounts = vec![ @@ -1383,43 +1385,61 @@ pub fn update_validator_list_balance( AccountMeta::new_readonly(sysvar::stake_history::id(), false), AccountMeta::new_readonly(stake::program::id(), false), ]; - accounts.append( - &mut validator_vote_accounts - .iter() - .flat_map(|vote_account_address| { - let validator_stake_info = validator_list.find(vote_account_address); - if let Some(validator_stake_info) = validator_stake_info { - let (validator_stake_account, _) = find_stake_program_address( - program_id, - vote_account_address, - stake_pool, - NonZeroU32::new(validator_stake_info.validator_seed_suffix.into()), - ); - let (transient_stake_account, _) = find_transient_stake_program_address( - program_id, - vote_account_address, - stake_pool, - validator_stake_info.transient_seed_suffix.into(), - ); - vec![ - AccountMeta::new(validator_stake_account, false), - AccountMeta::new(transient_stake_account, false), - ] - } else { - vec![] - } - }) - .collect::>(), - ); + let data = StakePoolInstruction::UpdateValidatorListBalance { + start_index: start_index.try_into().unwrap(), + no_merge, + } + .try_to_vec() + .unwrap(); + if start_index >= validator_list.validators.len() { + return Instruction { + program_id: *program_id, + accounts, + data, + }; + } + let validator_list_subslice = match validator_list + .validators + .get(start_index..start_index + len) + { + Some(s) => s, + None => { + return Instruction { + program_id: *program_id, + accounts, + data, + } + } + }; + accounts.extend(validator_list_subslice.iter().flat_map( + |ValidatorStakeInfo { + vote_account_address, + validator_seed_suffix, + transient_seed_suffix, + .. + }| { + let (validator_stake_account, _) = find_stake_program_address( + program_id, + vote_account_address, + stake_pool, + NonZeroU32::new((*validator_seed_suffix).into()), + ); + let (transient_stake_account, _) = find_transient_stake_program_address( + program_id, + vote_account_address, + stake_pool, + (*transient_seed_suffix).into(), + ); + [ + AccountMeta::new(validator_stake_account, false), + AccountMeta::new(transient_stake_account, false), + ] + }, + )); Instruction { program_id: *program_id, accounts, - data: StakePoolInstruction::UpdateValidatorListBalance { - start_index, - no_merge, - } - .try_to_vec() - .unwrap(), + data, } } @@ -1482,18 +1502,15 @@ pub fn update_stake_pool( stake_pool_address: &Pubkey, no_merge: bool, ) -> (Vec, Vec) { - let vote_accounts: Vec = validator_list - .validators - .iter() - .map(|item| item.vote_account_address) - .collect(); - let (withdraw_authority, _) = find_withdraw_authority_program_address(program_id, stake_pool_address); let mut update_list_instructions: Vec = vec![]; - let mut start_index = 0; - for accounts_chunk in vote_accounts.chunks(MAX_VALIDATORS_TO_UPDATE) { + for (i, chunk) in validator_list + .validators + .chunks(MAX_VALIDATORS_TO_UPDATE) + .enumerate() + { update_list_instructions.push(update_validator_list_balance( program_id, stake_pool_address, @@ -1501,11 +1518,10 @@ pub fn update_stake_pool( &stake_pool.validator_list, &stake_pool.reserve_stake, validator_list, - accounts_chunk, - start_index, + chunk.len(), + i * MAX_VALIDATORS_TO_UPDATE, no_merge, )); - start_index = start_index.saturating_add(MAX_VALIDATORS_TO_UPDATE as u32); } let final_instructions = vec![ diff --git a/stake-pool/program/tests/decrease.rs b/stake-pool/program/tests/decrease.rs index fe6398ae7b6..2e31c644844 100644 --- a/stake-pool/program/tests/decrease.rs +++ b/stake-pool/program/tests/decrease.rs @@ -624,7 +624,6 @@ async fn fail_additional_with_increasing() { &mut context.banks_client, &context.payer, &last_blockhash, - &[validator_stake.vote.pubkey()], false, ) .await; diff --git a/stake-pool/program/tests/deposit.rs b/stake-pool/program/tests/deposit.rs index 74e96194fa1..23bb69dc9cc 100644 --- a/stake-pool/program/tests/deposit.rs +++ b/stake-pool/program/tests/deposit.rs @@ -94,7 +94,6 @@ async fn setup( &mut context.banks_client, &context.payer, &context.last_blockhash, - &[validator_stake_account.vote.pubkey()], false, ) .await; diff --git a/stake-pool/program/tests/deposit_edge_cases.rs b/stake-pool/program/tests/deposit_edge_cases.rs index 86ae9c27d56..c893e0ce540 100644 --- a/stake-pool/program/tests/deposit_edge_cases.rs +++ b/stake-pool/program/tests/deposit_edge_cases.rs @@ -87,7 +87,6 @@ async fn setup( &mut context.banks_client, &context.payer, &context.last_blockhash, - &[validator_stake_account.vote.pubkey()], false, ) .await; diff --git a/stake-pool/program/tests/force_destake.rs b/stake-pool/program/tests/force_destake.rs index f54bd02db35..0b86a91f5ca 100644 --- a/stake-pool/program/tests/force_destake.rs +++ b/stake-pool/program/tests/force_destake.rs @@ -155,7 +155,6 @@ async fn success_update() { &mut context.banks_client, &context.payer, &context.last_blockhash, - &[voter_pubkey], false, ) .await; @@ -272,7 +271,6 @@ async fn success_remove_validator() { &mut context.banks_client, &context.payer, &context.last_blockhash, - &[voter_pubkey], false, ) .await; @@ -311,7 +309,6 @@ async fn success_remove_validator() { &mut context.banks_client, &context.payer, &context.last_blockhash, - &[voter_pubkey], false, ) .await; diff --git a/stake-pool/program/tests/helpers/mod.rs b/stake-pool/program/tests/helpers/mod.rs index 0bf6c1e18bb..598a3695323 100644 --- a/stake-pool/program/tests/helpers/mod.rs +++ b/stake-pool/program/tests/helpers/mod.rs @@ -1,5 +1,7 @@ #![allow(dead_code)] +use spl_stake_pool::MAX_VALIDATORS_TO_UPDATE; + use { borsh::{BorshDeserialize, BorshSerialize}, solana_program::{ @@ -1412,7 +1414,7 @@ impl StakePoolAccounts { banks_client: &mut BanksClient, payer: &Keypair, recent_blockhash: &Hash, - validator_vote_accounts: &[Pubkey], + len: usize, no_merge: bool, ) -> Option { let validator_list = self.get_validator_list(banks_client).await; @@ -1423,7 +1425,7 @@ impl StakePoolAccounts { &self.validator_list.pubkey(), &self.reserve_stake.pubkey(), &validator_list, - validator_vote_accounts, + len, 0, no_merge, )]; @@ -1501,22 +1503,28 @@ impl StakePoolAccounts { banks_client: &mut BanksClient, payer: &Keypair, recent_blockhash: &Hash, - validator_vote_accounts: &[Pubkey], no_merge: bool, ) -> Option { let validator_list = self.get_validator_list(banks_client).await; - let mut instructions = vec![ - instruction::update_validator_list_balance( + let mut instructions = vec![]; + for (i, chunk) in validator_list + .validators + .chunks(MAX_VALIDATORS_TO_UPDATE) + .enumerate() + { + instructions.push(instruction::update_validator_list_balance( &id(), &self.stake_pool.pubkey(), &self.withdraw_authority, &self.validator_list.pubkey(), &self.reserve_stake.pubkey(), &validator_list, - validator_vote_accounts, - 0, + chunk.len(), + i * MAX_VALIDATORS_TO_UPDATE, no_merge, - ), + )); + } + instructions.extend([ instruction::update_stake_pool_balance( &id(), &self.stake_pool.pubkey(), @@ -1532,7 +1540,7 @@ impl StakePoolAccounts { &self.stake_pool.pubkey(), &self.validator_list.pubkey(), ), - ]; + ]); self.maybe_add_compute_budget_instruction(&mut instructions); let transaction = Transaction::new_signed_with_payer( &instructions, diff --git a/stake-pool/program/tests/huge_pool.rs b/stake-pool/program/tests/huge_pool.rs index e87d3339772..5c1d0e32360 100644 --- a/stake-pool/program/tests/huge_pool.rs +++ b/stake-pool/program/tests/huge_pool.rs @@ -172,7 +172,7 @@ async fn setup( #[test_case(MAX_POOL_SIZE; "no-compute-budget")] #[tokio::test] async fn update(max_validators: u32) { - let (mut context, stake_pool_accounts, vote_account_pubkeys, _, _, _, _) = + let (mut context, stake_pool_accounts, _, _, _, _, _) = setup(max_validators, max_validators, STAKE_AMOUNT).await; let error = stake_pool_accounts @@ -180,7 +180,7 @@ async fn update(max_validators: u32) { &mut context.banks_client, &context.payer, &context.last_blockhash, - &vote_account_pubkeys[0..MAX_VALIDATORS_TO_UPDATE], + MAX_VALIDATORS_TO_UPDATE, false, /* no_merge */ ) .await; @@ -335,7 +335,7 @@ async fn remove_validator_from_pool(max_validators: u32) { &mut context.banks_client, &context.payer, &context.last_blockhash, - &[first_vote], + 1, false, /* no_merge */ ) .await; @@ -348,8 +348,8 @@ async fn remove_validator_from_pool(max_validators: u32) { &stake_pool_accounts.validator_list.pubkey(), &stake_pool_accounts.reserve_stake.pubkey(), &validator_list, - &[middle_vote], - middle_index as u32, + 1, + middle_index, /* no_merge = */ false, )]; stake_pool_accounts.maybe_add_compute_budget_instruction(&mut instructions); @@ -373,8 +373,8 @@ async fn remove_validator_from_pool(max_validators: u32) { &stake_pool_accounts.validator_list.pubkey(), &stake_pool_accounts.reserve_stake.pubkey(), &validator_list, - &[last_vote], - last_index as u32, + 1, + last_index, /* no_merge = */ false, )]; stake_pool_accounts.maybe_add_compute_budget_instruction(&mut instructions); diff --git a/stake-pool/program/tests/increase.rs b/stake-pool/program/tests/increase.rs index bd83885e496..67fac5f119b 100644 --- a/stake-pool/program/tests/increase.rs +++ b/stake-pool/program/tests/increase.rs @@ -548,7 +548,6 @@ async fn fail_additional_with_decreasing() { &mut context.banks_client, &context.payer, &last_blockhash, - &[validator_stake.vote.pubkey()], false, ) .await; diff --git a/stake-pool/program/tests/redelegate.rs b/stake-pool/program/tests/redelegate.rs index a9f12f5fd4f..3256eef1155 100644 --- a/stake-pool/program/tests/redelegate.rs +++ b/stake-pool/program/tests/redelegate.rs @@ -92,10 +92,6 @@ async fn setup( &mut context.banks_client, &context.payer, &context.last_blockhash, - &[ - source_validator_stake.vote.pubkey(), - destination_validator_stake.vote.pubkey(), - ], false, ) .await; @@ -296,10 +292,6 @@ async fn success() { &mut context.banks_client, &context.payer, &last_blockhash, - &[ - source_validator_stake.vote.pubkey(), - destination_validator_stake.vote.pubkey(), - ], false, ) .await; @@ -548,10 +540,6 @@ async fn success_with_increasing_stake() { &mut context.banks_client, &context.payer, &last_blockhash, - &[ - source_validator_stake.vote.pubkey(), - destination_validator_stake.vote.pubkey(), - ], false, ) .await; @@ -638,10 +626,6 @@ async fn fail_with_decreasing_stake() { &mut context.banks_client, &context.payer, &last_blockhash, - &[ - source_validator_stake.vote.pubkey(), - destination_validator_stake.vote.pubkey(), - ], false, ) .await; @@ -1059,10 +1043,6 @@ async fn fail_redelegate_twice() { &mut context.banks_client, &context.payer, &last_blockhash, - &[ - source_validator_stake.vote.pubkey(), - destination_validator_stake.vote.pubkey(), - ], false, ) .await; diff --git a/stake-pool/program/tests/set_epoch_fee.rs b/stake-pool/program/tests/set_epoch_fee.rs index db3b039ae99..533cad884a9 100644 --- a/stake-pool/program/tests/set_epoch_fee.rs +++ b/stake-pool/program/tests/set_epoch_fee.rs @@ -87,7 +87,6 @@ async fn success() { &mut context.banks_client, &context.payer, &context.last_blockhash, - &[], false, ) .await; @@ -112,7 +111,6 @@ async fn success() { &mut context.banks_client, &context.payer, &last_blockhash, - &[], false, ) .await; diff --git a/stake-pool/program/tests/set_withdrawal_fee.rs b/stake-pool/program/tests/set_withdrawal_fee.rs index 5c548b0adc9..a88a9ecc638 100644 --- a/stake-pool/program/tests/set_withdrawal_fee.rs +++ b/stake-pool/program/tests/set_withdrawal_fee.rs @@ -117,7 +117,6 @@ async fn success() { &mut context.banks_client, &context.payer, &context.last_blockhash, - &[], false, ) .await; @@ -150,7 +149,6 @@ async fn success() { &mut context.banks_client, &context.payer, &last_blockhash, - &[], false, ) .await; @@ -242,7 +240,6 @@ async fn success_fee_cannot_increase_more_than_once() { &mut context.banks_client, &context.payer, &context.last_blockhash, - &[], false, ) .await; @@ -275,7 +272,6 @@ async fn success_fee_cannot_increase_more_than_once() { &mut context.banks_client, &context.payer, &last_blockhash, - &[], false, ) .await; @@ -443,7 +439,6 @@ async fn success_reset_fee_after_one_epoch() { &mut context.banks_client, &context.payer, &context.last_blockhash, - &[], false, ) .await; @@ -600,7 +595,6 @@ async fn success_increase_fee_from_0() { &mut context.banks_client, &context.payer, &context.last_blockhash, - &[], false, ) .await; @@ -633,7 +627,6 @@ async fn success_increase_fee_from_0() { &mut context.banks_client, &context.payer, &last_blockhash, - &[], false, ) .await; diff --git a/stake-pool/program/tests/update_stake_pool_balance.rs b/stake-pool/program/tests/update_stake_pool_balance.rs index 50f9c3ee618..92dd797e49a 100644 --- a/stake-pool/program/tests/update_stake_pool_balance.rs +++ b/stake-pool/program/tests/update_stake_pool_balance.rs @@ -5,9 +5,7 @@ mod helpers; use { helpers::*, - solana_program::{ - borsh0_10::try_from_slice_unchecked, instruction::InstructionError, pubkey::Pubkey, - }, + solana_program::{borsh0_10::try_from_slice_unchecked, instruction::InstructionError}, solana_program_test::*, solana_sdk::{ hash::Hash, @@ -180,11 +178,6 @@ async fn success() { &mut context.banks_client, &context.payer, &last_blockhash, - stake_accounts - .iter() - .map(|v| v.vote.pubkey()) - .collect::>() - .as_slice(), false, ) .await; @@ -293,11 +286,6 @@ async fn success_absorbing_extra_lamports() { &mut context.banks_client, &context.payer, &last_blockhash, - stake_accounts - .iter() - .map(|v| v.vote.pubkey()) - .collect::>() - .as_slice(), false, ) .await; diff --git a/stake-pool/program/tests/update_validator_list_balance.rs b/stake-pool/program/tests/update_validator_list_balance.rs index 89d6b5f62df..9a6043598cb 100644 --- a/stake-pool/program/tests/update_validator_list_balance.rs +++ b/stake-pool/program/tests/update_validator_list_balance.rs @@ -5,7 +5,7 @@ mod helpers; use { helpers::*, - solana_program::{borsh0_10::try_from_slice_unchecked, program_pack::Pack, pubkey::Pubkey}, + solana_program::{borsh0_10::try_from_slice_unchecked, program_pack::Pack}, solana_program_test::*, solana_sdk::{hash::Hash, signature::Signer, stake::state::StakeStateV2}, spl_stake_pool::{ @@ -107,11 +107,6 @@ async fn setup( &mut context.banks_client, &context.payer, &context.last_blockhash, - stake_accounts - .iter() - .map(|v| v.vote.pubkey()) - .collect::>() - .as_slice(), false, ) .await; @@ -140,11 +135,6 @@ async fn setup( &mut context.banks_client, &context.payer, &last_blockhash, - stake_accounts - .iter() - .map(|v| v.vote.pubkey()) - .collect::>() - .as_slice(), false, ) .await; @@ -223,11 +213,6 @@ async fn success_with_normal() { &mut context.banks_client, &context.payer, &last_blockhash, - stake_accounts - .iter() - .map(|v| v.vote.pubkey()) - .collect::>() - .as_slice(), false, ) .await; @@ -299,11 +284,6 @@ async fn merge_into_reserve() { &mut context.banks_client, &context.payer, &last_blockhash, - stake_accounts - .iter() - .map(|v| v.vote.pubkey()) - .collect::>() - .as_slice(), false, ) .await; @@ -339,11 +319,6 @@ async fn merge_into_reserve() { &mut context.banks_client, &context.payer, &last_blockhash, - stake_accounts - .iter() - .map(|v| v.vote.pubkey()) - .collect::>() - .as_slice(), false, ) .await; @@ -435,11 +410,6 @@ async fn merge_into_validator_stake() { &mut context.banks_client, &context.payer, &last_blockhash, - stake_accounts - .iter() - .map(|v| v.vote.pubkey()) - .collect::>() - .as_slice(), false, ) .await; @@ -475,11 +445,6 @@ async fn merge_into_validator_stake() { &mut context.banks_client, &context.payer, &last_blockhash, - stake_accounts - .iter() - .map(|v| v.vote.pubkey()) - .collect::>() - .as_slice(), false, ) .await; @@ -591,11 +556,6 @@ async fn merge_transient_stake_after_remove() { &mut context.banks_client, &context.payer, &last_blockhash, - stake_accounts - .iter() - .map(|v| v.vote.pubkey()) - .collect::>() - .as_slice(), true, ) .await; @@ -628,11 +588,7 @@ async fn merge_transient_stake_after_remove() { &mut context.banks_client, &context.payer, &last_blockhash, - stake_accounts - .iter() - .map(|v| v.vote.pubkey()) - .collect::>() - .as_slice(), + validator_list.validators.len(), false, ) .await; @@ -706,16 +662,8 @@ async fn merge_transient_stake_after_remove() { #[tokio::test] async fn success_with_burned_tokens() { let num_validators = 5; - let ( - mut context, - last_blockhash, - stake_pool_accounts, - stake_accounts, - deposit_accounts, - _, - _, - mut slot, - ) = setup(num_validators).await; + let (mut context, last_blockhash, stake_pool_accounts, _, deposit_accounts, _, _, mut slot) = + setup(num_validators).await; let mint_info = get_account( &mut context.banks_client, @@ -762,11 +710,6 @@ async fn success_with_burned_tokens() { &mut context.banks_client, &context.payer, &last_blockhash, - stake_accounts - .iter() - .map(|v| v.vote.pubkey()) - .collect::>() - .as_slice(), false, ) .await; diff --git a/stake-pool/program/tests/update_validator_list_balance_hijack.rs b/stake-pool/program/tests/update_validator_list_balance_hijack.rs index ad444b20e2b..bfb16b9b0e5 100644 --- a/stake-pool/program/tests/update_validator_list_balance_hijack.rs +++ b/stake-pool/program/tests/update_validator_list_balance_hijack.rs @@ -109,11 +109,6 @@ async fn setup( &mut context.banks_client, &context.payer, &context.last_blockhash, - stake_accounts - .iter() - .map(|v| v.vote.pubkey()) - .collect::>() - .as_slice(), false, ) .await; @@ -143,11 +138,6 @@ async fn setup( &mut context.banks_client, &context.payer, &last_blockhash, - stake_accounts - .iter() - .map(|v| v.vote.pubkey()) - .collect::>() - .as_slice(), false, ) .await; @@ -258,7 +248,7 @@ async fn check_ignored_hijacked_transient_stake( &stake_pool_accounts.validator_list.pubkey(), &stake_pool_accounts.reserve_stake.pubkey(), &validator_list, - &[stake_account.vote.pubkey()], + 1, 0, /* no_merge = */ false, ), @@ -310,11 +300,6 @@ async fn check_ignored_hijacked_transient_stake( &mut context.banks_client, &context.payer, &last_blockhash, - stake_accounts - .iter() - .map(|v| v.vote.pubkey()) - .collect::>() - .as_slice(), false, ) .await; @@ -427,7 +412,7 @@ async fn check_ignored_hijacked_validator_stake( &stake_pool_accounts.validator_list.pubkey(), &stake_pool_accounts.reserve_stake.pubkey(), &validator_list, - &[stake_account.vote.pubkey()], + 1, 0, /* no_merge = */ false, ), @@ -479,11 +464,6 @@ async fn check_ignored_hijacked_validator_stake( &mut context.banks_client, &context.payer, &last_blockhash, - stake_accounts - .iter() - .map(|v| v.vote.pubkey()) - .collect::>() - .as_slice(), false, ) .await; diff --git a/stake-pool/program/tests/vsa_remove.rs b/stake-pool/program/tests/vsa_remove.rs index febada59196..922f285eb92 100644 --- a/stake-pool/program/tests/vsa_remove.rs +++ b/stake-pool/program/tests/vsa_remove.rs @@ -87,7 +87,6 @@ async fn success() { &mut context.banks_client, &context.payer, &context.last_blockhash, - &[validator_stake.vote.pubkey()], false, ) .await; @@ -267,7 +266,6 @@ async fn fail_double_remove() { &mut context.banks_client, &context.payer, &context.last_blockhash, - &[validator_stake.vote.pubkey()], false, ) .await; @@ -567,7 +565,6 @@ async fn success_with_deactivating_transient_stake() { &mut context.banks_client, &context.payer, &context.last_blockhash, - &[validator_stake.vote.pubkey()], false, ) .await; @@ -629,7 +626,6 @@ async fn success_resets_preferred_validator() { &mut context.banks_client, &context.payer, &context.last_blockhash, - &[validator_stake.vote.pubkey()], false, ) .await; @@ -701,7 +697,6 @@ async fn success_with_hijacked_transient_account() { &mut context.banks_client, &context.payer, &context.last_blockhash, - &[validator_stake.vote.pubkey()], false, ) .await; @@ -746,7 +741,7 @@ async fn success_with_hijacked_transient_account() { &stake_pool_accounts.validator_list.pubkey(), &stake_pool_accounts.reserve_stake.pubkey(), &validator_list, - &[validator_stake.vote.pubkey()], + 1, 0, /* no_merge = */ false, ), @@ -822,7 +817,6 @@ async fn success_with_hijacked_transient_account() { &mut context.banks_client, &context.payer, &context.last_blockhash, - &[validator_stake.vote.pubkey()], false, ) .await; diff --git a/stake-pool/program/tests/withdraw_edge_cases.rs b/stake-pool/program/tests/withdraw_edge_cases.rs index 5390bce5ed5..919d210777a 100644 --- a/stake-pool/program/tests/withdraw_edge_cases.rs +++ b/stake-pool/program/tests/withdraw_edge_cases.rs @@ -53,7 +53,6 @@ async fn fail_remove_validator() { &mut context.banks_client, &context.payer, &context.last_blockhash, - &[validator_stake.vote.pubkey()], false, ) .await; @@ -119,7 +118,6 @@ async fn success_remove_validator(multiple: u64) { &mut context.banks_client, &context.payer, &context.last_blockhash, - &[validator_stake.vote.pubkey()], false, ) .await; @@ -163,7 +161,6 @@ async fn success_remove_validator(multiple: u64) { &mut context.banks_client, &context.payer, &last_blockhash, - &[validator_stake.vote.pubkey()], false, ) .await; @@ -271,7 +268,6 @@ async fn fail_with_reserve() { &mut context.banks_client, &context.payer, &context.last_blockhash, - &[validator_stake.vote.pubkey()], false, ) .await; @@ -342,7 +338,6 @@ async fn success_with_reserve() { &mut context.banks_client, &context.payer, &context.last_blockhash, - &[validator_stake.vote.pubkey()], false, ) .await; @@ -768,7 +763,6 @@ async fn success_with_small_preferred_withdraw() { &mut context.banks_client, &context.payer, &last_blockhash, - &[validator_stake.vote.pubkey()], false, ) .await; @@ -843,10 +837,6 @@ async fn success_with_small_preferred_withdraw() { &mut context.banks_client, &context.payer, &last_blockhash, - &[ - validator_stake.vote.pubkey(), - preferred_validator.vote.pubkey(), - ], false, ) .await; From 05a5066739aa2c724abe815130a07bb00547180e Mon Sep 17 00:00:00 2001 From: billythedummy Date: Thu, 4 Jan 2024 12:49:01 +0800 Subject: [PATCH 02/10] nightly fmt --- stake-pool/program/src/instruction.rs | 4 +--- stake-pool/program/tests/helpers/mod.rs | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/stake-pool/program/src/instruction.rs b/stake-pool/program/src/instruction.rs index b4441caacd0..51e327023d4 100644 --- a/stake-pool/program/src/instruction.rs +++ b/stake-pool/program/src/instruction.rs @@ -2,15 +2,13 @@ #![allow(clippy::too_many_arguments)] -use crate::state::ValidatorStakeInfo; - use { crate::{ find_deposit_authority_program_address, find_ephemeral_stake_program_address, find_stake_program_address, find_transient_stake_program_address, find_withdraw_authority_program_address, inline_mpl_token_metadata::{self, pda::find_metadata_account}, - state::{Fee, FeeType, StakePool, ValidatorList}, + state::{Fee, FeeType, StakePool, ValidatorList, ValidatorStakeInfo}, MAX_VALIDATORS_TO_UPDATE, }, borsh::{BorshDeserialize, BorshSchema, BorshSerialize}, diff --git a/stake-pool/program/tests/helpers/mod.rs b/stake-pool/program/tests/helpers/mod.rs index 598a3695323..cf33cea1263 100644 --- a/stake-pool/program/tests/helpers/mod.rs +++ b/stake-pool/program/tests/helpers/mod.rs @@ -1,7 +1,5 @@ #![allow(dead_code)] -use spl_stake_pool::MAX_VALIDATORS_TO_UPDATE; - use { borsh::{BorshDeserialize, BorshSerialize}, solana_program::{ @@ -34,7 +32,7 @@ use { instruction, minimum_delegation, processor::Processor, state::{self, FeeType, FutureEpoch, StakePool, ValidatorList}, - MINIMUM_RESERVE_LAMPORTS, + MAX_VALIDATORS_TO_UPDATE, MINIMUM_RESERVE_LAMPORTS, }, spl_token_2022::{ extension::{ExtensionType, StateWithExtensionsOwned}, From e25893957648b44a4b3b67bae4015fbf6f622a79 Mon Sep 17 00:00:00 2001 From: billythedummy Date: Thu, 4 Jan 2024 13:03:39 +0800 Subject: [PATCH 03/10] fix clippy --- stake-pool/program/src/instruction.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stake-pool/program/src/instruction.rs b/stake-pool/program/src/instruction.rs index 51e327023d4..a13d0c2f9d0 100644 --- a/stake-pool/program/src/instruction.rs +++ b/stake-pool/program/src/instruction.rs @@ -1398,7 +1398,7 @@ pub fn update_validator_list_balance( } let validator_list_subslice = match validator_list .validators - .get(start_index..start_index + len) + .get(start_index..start_index.saturating_add(len)) { Some(s) => s, None => { @@ -1517,7 +1517,7 @@ pub fn update_stake_pool( &stake_pool.reserve_stake, validator_list, chunk.len(), - i * MAX_VALIDATORS_TO_UPDATE, + i.saturating_mul(MAX_VALIDATORS_TO_UPDATE), no_merge, )); } From 55859088a515b2b251137621af2d771ef574363a Mon Sep 17 00:00:00 2001 From: billythedummy Date: Thu, 4 Jan 2024 13:25:40 +0800 Subject: [PATCH 04/10] update stale validator_list_balance only if not force --- stake-pool/cli/src/main.rs | 52 ++++++++++++++++++++++++--- stake-pool/program/src/instruction.rs | 50 +++++++++++++++++++++----- 2 files changed, 90 insertions(+), 12 deletions(-) diff --git a/stake-pool/cli/src/main.rs b/stake-pool/cli/src/main.rs index 60ab61c6b2e..775042ddc4d 100644 --- a/stake-pool/cli/src/main.rs +++ b/stake-pool/cli/src/main.rs @@ -47,10 +47,13 @@ use { spl_stake_pool::{ self, find_stake_program_address, find_transient_stake_program_address, find_withdraw_authority_program_address, - instruction::{FundingType, PreferredValidatorType}, + instruction::{ + cleanup_removed_validator_entries, update_stake_pool_balance, + update_stale_validator_list_balance, FundingType, PreferredValidatorType, + }, minimum_delegation, state::{Fee, FeeType, StakePool, ValidatorList, ValidatorStakeInfo}, - MINIMUM_RESERVE_LAMPORTS, + MAX_VALIDATORS_TO_UPDATE, MINIMUM_RESERVE_LAMPORTS, }, std::{cmp::Ordering, num::NonZeroU32, process::exit, rc::Rc}, }; @@ -1158,14 +1161,55 @@ fn command_update( let validator_list = get_validator_list(&config.rpc_client, &stake_pool.validator_list)?; - let (mut update_list_instructions, final_instructions) = + let (mut update_list_instructions, final_instructions) = if force { spl_stake_pool::instruction::update_stake_pool( &spl_stake_pool::id(), &stake_pool, &validator_list, stake_pool_address, no_merge, - ); + ) + } else { + let (withdraw_authority, _) = + find_withdraw_authority_program_address(&spl_stake_pool::id(), stake_pool_address); + let update_list_instructions: Vec = validator_list + .validators + .chunks(MAX_VALIDATORS_TO_UPDATE) + .enumerate() + .filter_map(|(i, chunk)| { + update_stale_validator_list_balance( + &spl_stake_pool::id(), + stake_pool_address, + &withdraw_authority, + &stake_pool.validator_list, + &stake_pool.reserve_stake, + &validator_list, + chunk.len(), + i.saturating_mul(MAX_VALIDATORS_TO_UPDATE), + no_merge, + epoch_info.epoch, + ) + }) + .collect(); + let final_instructions = vec![ + update_stake_pool_balance( + &spl_stake_pool::id(), + stake_pool_address, + &withdraw_authority, + &stake_pool.validator_list, + &stake_pool.reserve_stake, + &stake_pool.manager_fee_account, + &stake_pool.pool_mint, + &stake_pool.token_program_id, + ), + cleanup_removed_validator_entries( + &spl_stake_pool::id(), + stake_pool_address, + &stake_pool.validator_list, + ), + ]; + (update_list_instructions, final_instructions) + }; let update_list_instructions_len = update_list_instructions.len(); if update_list_instructions_len > 0 { diff --git a/stake-pool/program/src/instruction.rs b/stake-pool/program/src/instruction.rs index a13d0c2f9d0..2d3db8700bc 100644 --- a/stake-pool/program/src/instruction.rs +++ b/stake-pool/program/src/instruction.rs @@ -15,7 +15,9 @@ use { solana_program::{ instruction::{AccountMeta, Instruction}, pubkey::Pubkey, - stake, system_program, sysvar, + stake, + stake_history::Epoch, + system_program, sysvar, }, std::num::NonZeroU32, }; @@ -1361,6 +1363,45 @@ pub fn decrease_additional_validator_stake_with_vote( ) } +/// Creates `UpdateValidatorListBalance` instruction (update validator stake +/// account balances) +/// +/// Returns None if all validators in the given chunk has already been updated +/// for this epoch, returns the required instruction otherwise. +pub fn update_stale_validator_list_balance( + program_id: &Pubkey, + stake_pool: &Pubkey, + stake_pool_withdraw_authority: &Pubkey, + validator_list_address: &Pubkey, + reserve_stake: &Pubkey, + validator_list: &ValidatorList, + len: usize, + start_index: usize, + no_merge: bool, + current_epoch: Epoch, +) -> Option { + let validator_list_subslice = validator_list + .validators + .get(start_index..start_index.saturating_add(len))?; + if validator_list_subslice.iter().all(|info| { + let last_update_epoch: u64 = info.last_update_epoch.into(); + last_update_epoch >= current_epoch + }) { + return None; + } + Some(update_validator_list_balance( + program_id, + stake_pool, + stake_pool_withdraw_authority, + validator_list_address, + reserve_stake, + validator_list, + len, + start_index, + no_merge, + )) +} + /// Creates `UpdateValidatorListBalance` instruction (update validator stake /// account balances) pub fn update_validator_list_balance( @@ -1389,13 +1430,6 @@ pub fn update_validator_list_balance( } .try_to_vec() .unwrap(); - if start_index >= validator_list.validators.len() { - return Instruction { - program_id: *program_id, - accounts, - data, - }; - } let validator_list_subslice = match validator_list .validators .get(start_index..start_index.saturating_add(len)) From 0c0ad9a0f31a174431ffdc8d9a30bea1c3d6f236 Mon Sep 17 00:00:00 2001 From: billythedummy Date: Sat, 20 Jan 2024 22:37:19 +0800 Subject: [PATCH 05/10] merge --- stake-pool/cli/src/main.rs | 9 +++++---- stake-pool/program/src/instruction.rs | 4 +--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/stake-pool/cli/src/main.rs b/stake-pool/cli/src/main.rs index 775042ddc4d..135865652f8 100644 --- a/stake-pool/cli/src/main.rs +++ b/stake-pool/cli/src/main.rs @@ -5,6 +5,7 @@ mod output; // use instruction::create_associated_token_account once ATA 1.0.5 is released #[allow(deprecated)] use spl_associated_token_account::create_associated_token_account; +use spl_stake_pool::instruction::update_validator_list_balance_chunk; use { crate::{ client::*, @@ -48,8 +49,8 @@ use { self, find_stake_program_address, find_transient_stake_program_address, find_withdraw_authority_program_address, instruction::{ - cleanup_removed_validator_entries, update_stake_pool_balance, - update_stale_validator_list_balance, FundingType, PreferredValidatorType, + cleanup_removed_validator_entries, update_stake_pool_balance, FundingType, + PreferredValidatorType, }, minimum_delegation, state::{Fee, FeeType, StakePool, ValidatorList, ValidatorStakeInfo}, @@ -1177,7 +1178,7 @@ fn command_update( .chunks(MAX_VALIDATORS_TO_UPDATE) .enumerate() .filter_map(|(i, chunk)| { - update_stale_validator_list_balance( + update_validator_list_balance_chunk( &spl_stake_pool::id(), stake_pool_address, &withdraw_authority, @@ -1187,8 +1188,8 @@ fn command_update( chunk.len(), i.saturating_mul(MAX_VALIDATORS_TO_UPDATE), no_merge, - epoch_info.epoch, ) + .ok() }) .collect(); let final_instructions = vec![ diff --git a/stake-pool/program/src/instruction.rs b/stake-pool/program/src/instruction.rs index f9b6c44a796..29175bb5e07 100644 --- a/stake-pool/program/src/instruction.rs +++ b/stake-pool/program/src/instruction.rs @@ -16,9 +16,7 @@ use { instruction::{AccountMeta, Instruction}, program_error::ProgramError, pubkey::Pubkey, - stake, - stake_history::Epoch, - system_program, sysvar, + stake, system_program, sysvar, }, std::num::NonZeroU32, }; From af28bf87690eb9facb1d4b462dada4d6d45c20ca Mon Sep 17 00:00:00 2001 From: billythedummy Date: Sat, 20 Jan 2024 22:38:23 +0800 Subject: [PATCH 06/10] fmt --- stake-pool/cli/src/main.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/stake-pool/cli/src/main.rs b/stake-pool/cli/src/main.rs index 135865652f8..8f44baea546 100644 --- a/stake-pool/cli/src/main.rs +++ b/stake-pool/cli/src/main.rs @@ -5,7 +5,6 @@ mod output; // use instruction::create_associated_token_account once ATA 1.0.5 is released #[allow(deprecated)] use spl_associated_token_account::create_associated_token_account; -use spl_stake_pool::instruction::update_validator_list_balance_chunk; use { crate::{ client::*, @@ -49,8 +48,8 @@ use { self, find_stake_program_address, find_transient_stake_program_address, find_withdraw_authority_program_address, instruction::{ - cleanup_removed_validator_entries, update_stake_pool_balance, FundingType, - PreferredValidatorType, + cleanup_removed_validator_entries, update_stake_pool_balance, + update_validator_list_balance_chunk, FundingType, PreferredValidatorType, }, minimum_delegation, state::{Fee, FeeType, StakePool, ValidatorList, ValidatorStakeInfo}, From 7dedc829a8a7638f5e6ffcc70ba5ae49cc97a99e Mon Sep 17 00:00:00 2001 From: billythedummy Date: Sat, 20 Jan 2024 22:40:48 +0800 Subject: [PATCH 07/10] merge conflict --- stake-pool/program/src/instruction.rs | 83 ++++++++++++--------------- 1 file changed, 36 insertions(+), 47 deletions(-) diff --git a/stake-pool/program/src/instruction.rs b/stake-pool/program/src/instruction.rs index 29175bb5e07..68b0946999c 100644 --- a/stake-pool/program/src/instruction.rs +++ b/stake-pool/program/src/instruction.rs @@ -1375,8 +1375,8 @@ pub fn update_validator_list_balance( validator_list_address: &Pubkey, reserve_stake: &Pubkey, validator_list: &ValidatorList, - len: usize, - start_index: usize, + validator_vote_accounts: &[Pubkey], + start_index: u32, no_merge: bool, ) -> Instruction { let mut accounts = vec![ @@ -1388,54 +1388,43 @@ pub fn update_validator_list_balance( AccountMeta::new_readonly(sysvar::stake_history::id(), false), AccountMeta::new_readonly(stake::program::id(), false), ]; - let data = StakePoolInstruction::UpdateValidatorListBalance { - start_index: start_index.try_into().unwrap(), - no_merge, - } - .try_to_vec() - .unwrap(); - let validator_list_subslice = match validator_list - .validators - .get(start_index..start_index.saturating_add(len)) - { - Some(s) => s, - None => { - return Instruction { - program_id: *program_id, - accounts, - data, - } - } - }; - accounts.extend(validator_list_subslice.iter().flat_map( - |ValidatorStakeInfo { - vote_account_address, - validator_seed_suffix, - transient_seed_suffix, - .. - }| { - let (validator_stake_account, _) = find_stake_program_address( - program_id, - vote_account_address, - stake_pool, - NonZeroU32::new((*validator_seed_suffix).into()), - ); - let (transient_stake_account, _) = find_transient_stake_program_address( - program_id, - vote_account_address, - stake_pool, - (*transient_seed_suffix).into(), - ); - [ - AccountMeta::new(validator_stake_account, false), - AccountMeta::new(transient_stake_account, false), - ] - }, - )); + accounts.append( + &mut validator_vote_accounts + .iter() + .flat_map(|vote_account_address| { + let validator_stake_info = validator_list.find(vote_account_address); + if let Some(validator_stake_info) = validator_stake_info { + let (validator_stake_account, _) = find_stake_program_address( + program_id, + vote_account_address, + stake_pool, + NonZeroU32::new(validator_stake_info.validator_seed_suffix.into()), + ); + let (transient_stake_account, _) = find_transient_stake_program_address( + program_id, + vote_account_address, + stake_pool, + validator_stake_info.transient_seed_suffix.into(), + ); + vec![ + AccountMeta::new(validator_stake_account, false), + AccountMeta::new(transient_stake_account, false), + ] + } else { + vec![] + } + }) + .collect::>(), + ); Instruction { program_id: *program_id, accounts, - data, + data: StakePoolInstruction::UpdateValidatorListBalance { + start_index, + no_merge, + } + .try_to_vec() + .unwrap(), } } From 10746e248a5ea9867d80ee6ec6677f05d7a2be99 Mon Sep 17 00:00:00 2001 From: billythedummy Date: Sat, 20 Jan 2024 23:20:32 +0800 Subject: [PATCH 08/10] restore stale ixs, add fresh flag to update --- stake-pool/cli/src/main.rs | 92 +++++++++-------------- stake-pool/program/src/instruction.rs | 101 ++++++++++++++++++++++++++ 2 files changed, 134 insertions(+), 59 deletions(-) diff --git a/stake-pool/cli/src/main.rs b/stake-pool/cli/src/main.rs index 8f44baea546..c91272856a9 100644 --- a/stake-pool/cli/src/main.rs +++ b/stake-pool/cli/src/main.rs @@ -47,13 +47,10 @@ use { spl_stake_pool::{ self, find_stake_program_address, find_transient_stake_program_address, find_withdraw_authority_program_address, - instruction::{ - cleanup_removed_validator_entries, update_stake_pool_balance, - update_validator_list_balance_chunk, FundingType, PreferredValidatorType, - }, + instruction::{FundingType, PreferredValidatorType}, minimum_delegation, state::{Fee, FeeType, StakePool, ValidatorList, ValidatorStakeInfo}, - MAX_VALIDATORS_TO_UPDATE, MINIMUM_RESERVE_LAMPORTS, + MINIMUM_RESERVE_LAMPORTS, }, std::{cmp::Ordering, num::NonZeroU32, process::exit, rc::Rc}, }; @@ -433,7 +430,7 @@ fn command_vsa_add( } if !config.no_update { - command_update(config, stake_pool_address, false, false)?; + command_update(config, stake_pool_address, false, false, false)?; } // iterate until a free account is found @@ -491,7 +488,7 @@ fn command_vsa_remove( vote_account: &Pubkey, ) -> CommandResult { if !config.no_update { - command_update(config, stake_pool_address, false, false)?; + command_update(config, stake_pool_address, false, false, false)?; } let stake_pool = get_stake_pool(&config.rpc_client, stake_pool_address)?; @@ -538,7 +535,7 @@ fn command_increase_validator_stake( ) -> CommandResult { let lamports = native_token::sol_to_lamports(amount); if !config.no_update { - command_update(config, stake_pool_address, false, false)?; + command_update(config, stake_pool_address, false, false, false)?; } let stake_pool = get_stake_pool(&config.rpc_client, stake_pool_address)?; @@ -577,7 +574,7 @@ fn command_decrease_validator_stake( ) -> CommandResult { let lamports = native_token::sol_to_lamports(amount); if !config.no_update { - command_update(config, stake_pool_address, false, false)?; + command_update(config, stake_pool_address, false, false, false)?; } let stake_pool = get_stake_pool(&config.rpc_client, stake_pool_address)?; @@ -674,7 +671,7 @@ fn command_deposit_stake( referrer_token_account: &Option, ) -> CommandResult { if !config.no_update { - command_update(config, stake_pool_address, false, false)?; + command_update(config, stake_pool_address, false, false, false)?; } let stake_pool = get_stake_pool(&config.rpc_client, stake_pool_address)?; @@ -805,7 +802,7 @@ fn command_deposit_all_stake( referrer_token_account: &Option, ) -> CommandResult { if !config.no_update { - command_update(config, stake_pool_address, false, false)?; + command_update(config, stake_pool_address, false, false, false)?; } let stake_addresses = get_all_stake(&config.rpc_client, stake_authority)?; @@ -948,7 +945,7 @@ fn command_deposit_sol( amount: f64, ) -> CommandResult { if !config.no_update { - command_update(config, stake_pool_address, false, false)?; + command_update(config, stake_pool_address, false, false, false)?; } let amount = native_token::sol_to_lamports(amount); @@ -1142,6 +1139,7 @@ fn command_update( stake_pool_address: &Pubkey, force: bool, no_merge: bool, + fresh: bool, ) -> CommandResult { if config.no_update { println!("Update requested, but --no-update flag specified, so doing nothing"); @@ -1161,7 +1159,7 @@ fn command_update( let validator_list = get_validator_list(&config.rpc_client, &stake_pool.validator_list)?; - let (mut update_list_instructions, final_instructions) = if force { + let (mut update_list_instructions, final_instructions) = if fresh { spl_stake_pool::instruction::update_stake_pool( &spl_stake_pool::id(), &stake_pool, @@ -1170,45 +1168,14 @@ fn command_update( no_merge, ) } else { - let (withdraw_authority, _) = - find_withdraw_authority_program_address(&spl_stake_pool::id(), stake_pool_address); - let update_list_instructions: Vec = validator_list - .validators - .chunks(MAX_VALIDATORS_TO_UPDATE) - .enumerate() - .filter_map(|(i, chunk)| { - update_validator_list_balance_chunk( - &spl_stake_pool::id(), - stake_pool_address, - &withdraw_authority, - &stake_pool.validator_list, - &stake_pool.reserve_stake, - &validator_list, - chunk.len(), - i.saturating_mul(MAX_VALIDATORS_TO_UPDATE), - no_merge, - ) - .ok() - }) - .collect(); - let final_instructions = vec![ - update_stake_pool_balance( - &spl_stake_pool::id(), - stake_pool_address, - &withdraw_authority, - &stake_pool.validator_list, - &stake_pool.reserve_stake, - &stake_pool.manager_fee_account, - &stake_pool.pool_mint, - &stake_pool.token_program_id, - ), - cleanup_removed_validator_entries( - &spl_stake_pool::id(), - stake_pool_address, - &stake_pool.validator_list, - ), - ]; - (update_list_instructions, final_instructions) + spl_stake_pool::instruction::update_stale_stake_pool( + &spl_stake_pool::id(), + &stake_pool, + &validator_list, + stake_pool_address, + no_merge, + epoch_info.epoch, + ) }; let update_list_instructions_len = update_list_instructions.len(); @@ -1404,7 +1371,7 @@ fn command_withdraw_stake( pool_amount: f64, ) -> CommandResult { if !config.no_update { - command_update(config, stake_pool_address, false, false)?; + command_update(config, stake_pool_address, false, false, false)?; } let stake_pool = get_stake_pool(&config.rpc_client, stake_pool_address)?; @@ -1675,7 +1642,7 @@ fn command_withdraw_sol( pool_amount: f64, ) -> CommandResult { if !config.no_update { - command_update(config, stake_pool_address, false, false)?; + command_update(config, stake_pool_address, false, false, false)?; } let stake_pool = get_stake_pool(&config.rpc_client, stake_pool_address)?; @@ -1792,7 +1759,7 @@ fn command_set_manager( new_fee_receiver: &Option, ) -> CommandResult { if !config.no_update { - command_update(config, stake_pool_address, false, false)?; + command_update(config, stake_pool_address, false, false, false)?; } let stake_pool = get_stake_pool(&config.rpc_client, stake_pool_address)?; @@ -1844,7 +1811,7 @@ fn command_set_staker( new_staker: &Pubkey, ) -> CommandResult { if !config.no_update { - command_update(config, stake_pool_address, false, false)?; + command_update(config, stake_pool_address, false, false, false)?; } let mut signers = vec![config.fee_payer.as_ref(), config.manager.as_ref()]; unique_signers!(signers); @@ -1869,7 +1836,7 @@ fn command_set_funding_authority( funding_type: FundingType, ) -> CommandResult { if !config.no_update { - command_update(config, stake_pool_address, false, false)?; + command_update(config, stake_pool_address, false, false, false)?; } let mut signers = vec![config.fee_payer.as_ref(), config.manager.as_ref()]; unique_signers!(signers); @@ -1894,7 +1861,7 @@ fn command_set_fee( new_fee: FeeType, ) -> CommandResult { if !config.no_update { - command_update(config, stake_pool_address, false, false)?; + command_update(config, stake_pool_address, false, false, false)?; } let mut signers = vec![config.fee_payer.as_ref(), config.manager.as_ref()]; unique_signers!(signers); @@ -2470,6 +2437,12 @@ fn main() { .takes_value(false) .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."), ) + .arg( + Arg::with_name("fresh") + .long("fresh") + .takes_value(false) + .help("If set, updates all validator balances on the validator list. Otherwise, this command only updates validator list balances that have not been updated for this epoch."), + ) ) .subcommand(SubCommand::with_name("withdraw-stake") .about("Withdraw active stake from the stake pool in exchange for pool tokens") @@ -2947,7 +2920,8 @@ fn main() { let stake_pool_address = pubkey_of(arg_matches, "pool").unwrap(); let no_merge = arg_matches.is_present("no_merge"); let force = arg_matches.is_present("force"); - command_update(&config, &stake_pool_address, force, no_merge) + let fresh = arg_matches.is_present("fresh"); + command_update(&config, &stake_pool_address, force, no_merge, fresh) } ("withdraw-stake", Some(arg_matches)) => { let stake_pool_address = pubkey_of(arg_matches, "pool").unwrap(); diff --git a/stake-pool/program/src/instruction.rs b/stake-pool/program/src/instruction.rs index 68b0946999c..9cb83bf0366 100644 --- a/stake-pool/program/src/instruction.rs +++ b/stake-pool/program/src/instruction.rs @@ -2,6 +2,8 @@ #![allow(clippy::too_many_arguments)] +use solana_program::stake_history::Epoch; + use { crate::{ find_deposit_authority_program_address, find_ephemeral_stake_program_address, @@ -1496,6 +1498,47 @@ pub fn update_validator_list_balance_chunk( }) } +/// Creates `UpdateValidatorListBalance` instruction (update validator stake +/// account balances) +/// +/// Returns `None` if all validators in the given chunk has already been updated +/// for this epoch, returns the required instruction otherwise. +pub fn update_stale_validator_list_balance_chunk( + program_id: &Pubkey, + stake_pool: &Pubkey, + stake_pool_withdraw_authority: &Pubkey, + validator_list_address: &Pubkey, + reserve_stake: &Pubkey, + validator_list: &ValidatorList, + len: usize, + start_index: usize, + no_merge: bool, + current_epoch: Epoch, +) -> Result, ProgramError> { + let validator_list_subslice = validator_list + .validators + .get(start_index..start_index.saturating_add(len)) + .ok_or(ProgramError::InvalidInstructionData)?; + if validator_list_subslice.iter().all(|info| { + let last_update_epoch: u64 = info.last_update_epoch.into(); + last_update_epoch >= current_epoch + }) { + return Ok(None); + } + update_validator_list_balance_chunk( + program_id, + stake_pool, + stake_pool_withdraw_authority, + validator_list_address, + reserve_stake, + validator_list, + len, + start_index, + no_merge, + ) + .map(Some) +} + /// Creates `UpdateStakePoolBalance` instruction (pool balance from the stake /// account list balances) pub fn update_stake_pool_balance( @@ -1599,6 +1642,64 @@ pub fn update_stake_pool( (update_list_instructions, final_instructions) } +/// Creates the `UpdateValidatorListBalance` instructions only for validators on `validator_list` +/// that have not been updated for this epoch, and the `UpdateStakePoolBalance` instruction +/// for fully updating the stake pool. +/// +/// Basically same as [`update_stake_pool`], but skips validators that are already updated for this epoch +pub fn update_stale_stake_pool( + program_id: &Pubkey, + stake_pool: &StakePool, + validator_list: &ValidatorList, + stake_pool_address: &Pubkey, + no_merge: bool, + current_epoch: Epoch, +) -> (Vec, Vec) { + let (withdraw_authority, _) = + find_withdraw_authority_program_address(program_id, stake_pool_address); + + let update_list_instructions = validator_list + .validators + .chunks(MAX_VALIDATORS_TO_UPDATE) + .enumerate() + .filter_map(|(i, chunk)| { + // unwrap-safety: chunk len and offset are derived + update_stale_validator_list_balance_chunk( + program_id, + stake_pool_address, + &withdraw_authority, + &stake_pool.validator_list, + &stake_pool.reserve_stake, + validator_list, + chunk.len(), + i.saturating_mul(MAX_VALIDATORS_TO_UPDATE), + no_merge, + current_epoch, + ) + .unwrap() + }) + .collect(); + + let final_instructions = vec![ + update_stake_pool_balance( + program_id, + stake_pool_address, + &withdraw_authority, + &stake_pool.validator_list, + &stake_pool.reserve_stake, + &stake_pool.manager_fee_account, + &stake_pool.pool_mint, + &stake_pool.token_program_id, + ), + cleanup_removed_validator_entries( + program_id, + stake_pool_address, + &stake_pool.validator_list, + ), + ]; + (update_list_instructions, final_instructions) +} + fn deposit_stake_internal( program_id: &Pubkey, stake_pool: &Pubkey, From c635779462c369d428b07bcecf1b7d442bd8124d Mon Sep 17 00:00:00 2001 From: billythedummy Date: Sat, 20 Jan 2024 23:23:31 +0800 Subject: [PATCH 09/10] fmt --- stake-pool/program/src/instruction.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/stake-pool/program/src/instruction.rs b/stake-pool/program/src/instruction.rs index 9cb83bf0366..4b8afd8e17b 100644 --- a/stake-pool/program/src/instruction.rs +++ b/stake-pool/program/src/instruction.rs @@ -2,8 +2,6 @@ #![allow(clippy::too_many_arguments)] -use solana_program::stake_history::Epoch; - use { crate::{ find_deposit_authority_program_address, find_ephemeral_stake_program_address, @@ -18,7 +16,9 @@ use { instruction::{AccountMeta, Instruction}, program_error::ProgramError, pubkey::Pubkey, - stake, system_program, sysvar, + stake, + stake_history::Epoch, + system_program, sysvar, }, std::num::NonZeroU32, }; @@ -1642,11 +1642,12 @@ pub fn update_stake_pool( (update_list_instructions, final_instructions) } -/// Creates the `UpdateValidatorListBalance` instructions only for validators on `validator_list` -/// that have not been updated for this epoch, and the `UpdateStakePoolBalance` instruction -/// for fully updating the stake pool. +/// Creates the `UpdateValidatorListBalance` instructions only for validators on +/// `validator_list` that have not been updated for this epoch, and the +/// `UpdateStakePoolBalance` instruction for fully updating the stake pool. /// -/// Basically same as [`update_stake_pool`], but skips validators that are already updated for this epoch +/// Basically same as [`update_stake_pool`], but skips validators that are +/// already updated for this epoch pub fn update_stale_stake_pool( program_id: &Pubkey, stake_pool: &StakePool, From 89c4c313c1a9dbb64d23f0fdc170e3555edcf458 Mon Sep 17 00:00:00 2001 From: billythedummy Date: Wed, 24 Jan 2024 13:41:31 +0800 Subject: [PATCH 10/10] fresh -> stale_only, update --force help --- stake-pool/cli/src/main.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/stake-pool/cli/src/main.rs b/stake-pool/cli/src/main.rs index c91272856a9..bb20ffde8ed 100644 --- a/stake-pool/cli/src/main.rs +++ b/stake-pool/cli/src/main.rs @@ -1139,7 +1139,7 @@ fn command_update( stake_pool_address: &Pubkey, force: bool, no_merge: bool, - fresh: bool, + stale_only: bool, ) -> CommandResult { if config.no_update { println!("Update requested, but --no-update flag specified, so doing nothing"); @@ -1159,22 +1159,22 @@ fn command_update( let validator_list = get_validator_list(&config.rpc_client, &stake_pool.validator_list)?; - let (mut update_list_instructions, final_instructions) = if fresh { - spl_stake_pool::instruction::update_stake_pool( + let (mut update_list_instructions, final_instructions) = if stale_only { + spl_stake_pool::instruction::update_stale_stake_pool( &spl_stake_pool::id(), &stake_pool, &validator_list, stake_pool_address, no_merge, + epoch_info.epoch, ) } else { - spl_stake_pool::instruction::update_stale_stake_pool( + spl_stake_pool::instruction::update_stake_pool( &spl_stake_pool::id(), &stake_pool, &validator_list, stake_pool_address, no_merge, - epoch_info.epoch, ) }; @@ -2429,7 +2429,7 @@ fn main() { Arg::with_name("force") .long("force") .takes_value(false) - .help("Update all balances, even if it has already been performed this epoch."), + .help("Update balances, even if it has already been performed this epoch."), ) .arg( Arg::with_name("no_merge") @@ -2438,10 +2438,10 @@ fn main() { .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."), ) .arg( - Arg::with_name("fresh") - .long("fresh") + Arg::with_name("stale_only") + .long("stale-only") .takes_value(false) - .help("If set, updates all validator balances on the validator list. Otherwise, this command only updates validator list balances that have not been updated for this epoch."), + .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."), ) ) .subcommand(SubCommand::with_name("withdraw-stake") @@ -2920,8 +2920,8 @@ fn main() { let stake_pool_address = pubkey_of(arg_matches, "pool").unwrap(); let no_merge = arg_matches.is_present("no_merge"); let force = arg_matches.is_present("force"); - let fresh = arg_matches.is_present("fresh"); - command_update(&config, &stake_pool_address, force, no_merge, fresh) + let stale_only = arg_matches.is_present("stale_only"); + command_update(&config, &stake_pool_address, force, no_merge, stale_only) } ("withdraw-stake", Some(arg_matches)) => { let stake_pool_address = pubkey_of(arg_matches, "pool").unwrap();