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

Commit 9aac29c

Browse files
authored
stake-pool: Add "IncreaseAdditionalValidatorStake" instruction (#3924)
* stake-pool: Add "IncreaseAdditionalValidatorStake" instruction * Address feedback * Always check transient stake account address
1 parent e0b70a9 commit 9aac29c

File tree

5 files changed

+738
-185
lines changed

5 files changed

+738
-185
lines changed

stake-pool/program/src/instruction.rs

Lines changed: 135 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44

55
use {
66
crate::{
7-
find_deposit_authority_program_address, find_stake_program_address,
8-
find_transient_stake_program_address, find_withdraw_authority_program_address,
7+
find_deposit_authority_program_address, find_ephemeral_stake_program_address,
8+
find_stake_program_address, find_transient_stake_program_address,
9+
find_withdraw_authority_program_address,
910
state::{Fee, FeeType, StakePool, ValidatorList},
1011
MAX_VALIDATORS_TO_UPDATE,
1112
},
@@ -401,6 +402,46 @@ pub enum StakePoolInstruction {
401402
/// URI of the uploaded metadata of the spl-token
402403
uri: String,
403404
},
405+
406+
/// (Staker only) Increase stake on a validator again in an epoch.
407+
///
408+
/// Works regardless if the transient stake account exists.
409+
///
410+
/// Internally, this instruction splits reserve stake into an ephemeral stake
411+
/// account, activates it, then merges or splits it into the transient stake
412+
/// account delegated to the appropriate validator. `UpdateValidatorListBalance`
413+
/// will do the work of merging once it's ready.
414+
///
415+
/// The minimum amount to move is rent-exemption plus
416+
/// `max(crate::MINIMUM_ACTIVE_STAKE, solana_program::stake::tools::get_minimum_delegation())`.
417+
///
418+
/// 0. `[]` Stake pool
419+
/// 1. `[s]` Stake pool staker
420+
/// 2. `[]` Stake pool withdraw authority
421+
/// 3. `[w]` Validator list
422+
/// 4. `[w]` Stake pool reserve stake
423+
/// 5. `[w]` Uninitialized ephemeral stake account to receive stake
424+
/// 6. `[w]` Transient stake account
425+
/// 7. `[]` Validator stake account
426+
/// 8. `[]` Validator vote account to delegate to
427+
/// 9. '[]' Clock sysvar
428+
/// 10. `[]` Stake History sysvar
429+
/// 11. `[]` Stake Config sysvar
430+
/// 12. `[]` System program
431+
/// 13. `[]` Stake program
432+
/// userdata: amount of lamports to increase on the given validator.
433+
/// The actual amount split into the transient stake account is:
434+
/// `lamports + stake_rent_exemption`
435+
/// The rent-exemption of the stake account is withdrawn back to the reserve
436+
/// after it is merged.
437+
IncreaseAdditionalValidatorStake {
438+
/// amount of lamports to increase on the given validator
439+
lamports: u64,
440+
/// seed used to create transient stake account
441+
transient_stake_seed: u64,
442+
/// seed used to create ephemeral account.
443+
ephemeral_stake_seed: u64,
444+
},
404445
}
405446

406447
/// Creates an 'initialize' instruction.
@@ -597,6 +638,52 @@ pub fn increase_validator_stake(
597638
}
598639
}
599640

641+
/// Creates `IncreaseAdditionalValidatorStake` instruction (rebalance from reserve account to
642+
/// transient account)
643+
pub fn increase_additional_validator_stake(
644+
program_id: &Pubkey,
645+
stake_pool: &Pubkey,
646+
staker: &Pubkey,
647+
stake_pool_withdraw_authority: &Pubkey,
648+
validator_list: &Pubkey,
649+
reserve_stake: &Pubkey,
650+
ephemeral_stake: &Pubkey,
651+
transient_stake: &Pubkey,
652+
validator_stake: &Pubkey,
653+
validator: &Pubkey,
654+
lamports: u64,
655+
transient_stake_seed: u64,
656+
ephemeral_stake_seed: u64,
657+
) -> Instruction {
658+
let accounts = vec![
659+
AccountMeta::new_readonly(*stake_pool, false),
660+
AccountMeta::new_readonly(*staker, true),
661+
AccountMeta::new_readonly(*stake_pool_withdraw_authority, false),
662+
AccountMeta::new(*validator_list, false),
663+
AccountMeta::new(*reserve_stake, false),
664+
AccountMeta::new(*ephemeral_stake, false),
665+
AccountMeta::new(*transient_stake, false),
666+
AccountMeta::new_readonly(*validator_stake, false),
667+
AccountMeta::new_readonly(*validator, false),
668+
AccountMeta::new_readonly(sysvar::clock::id(), false),
669+
AccountMeta::new_readonly(sysvar::stake_history::id(), false),
670+
AccountMeta::new_readonly(stake::config::id(), false),
671+
AccountMeta::new_readonly(system_program::id(), false),
672+
AccountMeta::new_readonly(stake::program::id(), false),
673+
];
674+
Instruction {
675+
program_id: *program_id,
676+
accounts,
677+
data: StakePoolInstruction::IncreaseAdditionalValidatorStake {
678+
lamports,
679+
transient_stake_seed,
680+
ephemeral_stake_seed,
681+
}
682+
.try_to_vec()
683+
.unwrap(),
684+
}
685+
}
686+
600687
/// Creates `SetPreferredDepositValidator` instruction
601688
pub fn set_preferred_validator(
602689
program_id: &Pubkey,
@@ -724,6 +811,52 @@ pub fn increase_validator_stake_with_vote(
724811
)
725812
}
726813

814+
/// Create an `IncreaseAdditionalValidatorStake` instruction given an existing
815+
/// stake pool and vote account
816+
pub fn increase_additional_validator_stake_with_vote(
817+
program_id: &Pubkey,
818+
stake_pool: &StakePool,
819+
stake_pool_address: &Pubkey,
820+
vote_account_address: &Pubkey,
821+
lamports: u64,
822+
validator_stake_seed: Option<NonZeroU32>,
823+
transient_stake_seed: u64,
824+
ephemeral_stake_seed: u64,
825+
) -> Instruction {
826+
let pool_withdraw_authority =
827+
find_withdraw_authority_program_address(program_id, stake_pool_address).0;
828+
let (ephemeral_stake_address, _) =
829+
find_ephemeral_stake_program_address(program_id, stake_pool_address, ephemeral_stake_seed);
830+
let (transient_stake_address, _) = find_transient_stake_program_address(
831+
program_id,
832+
vote_account_address,
833+
stake_pool_address,
834+
transient_stake_seed,
835+
);
836+
let (validator_stake_address, _) = find_stake_program_address(
837+
program_id,
838+
vote_account_address,
839+
stake_pool_address,
840+
validator_stake_seed,
841+
);
842+
843+
increase_additional_validator_stake(
844+
program_id,
845+
stake_pool_address,
846+
&stake_pool.staker,
847+
&pool_withdraw_authority,
848+
&stake_pool.validator_list,
849+
&stake_pool.reserve_stake,
850+
&ephemeral_stake_address,
851+
&transient_stake_address,
852+
&validator_stake_address,
853+
vote_account_address,
854+
lamports,
855+
transient_stake_seed,
856+
ephemeral_stake_seed,
857+
)
858+
}
859+
727860
/// Create a `DecreaseValidatorStake` instruction given an existing stake pool and
728861
/// vote account
729862
pub fn decrease_validator_stake_with_vote(

stake-pool/program/src/lib.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ const AUTHORITY_WITHDRAW: &[u8] = b"withdraw";
2828
/// Seed for transient stake account
2929
const TRANSIENT_STAKE_SEED_PREFIX: &[u8] = b"transient";
3030

31+
/// Seed for ephemeral stake account
32+
const EPHEMERAL_STAKE_SEED_PREFIX: &[u8] = b"ephemeral";
33+
3134
/// Minimum amount of staked lamports required in a validator stake account to allow
3235
/// for merges without a mismatch on credits observed
3336
pub const MINIMUM_ACTIVE_STAKE: u64 = 1_000_000;
@@ -138,6 +141,22 @@ pub fn find_transient_stake_program_address(
138141
)
139142
}
140143

144+
/// Generates the ephemeral program address for stake pool redelegation
145+
pub fn find_ephemeral_stake_program_address(
146+
program_id: &Pubkey,
147+
stake_pool_address: &Pubkey,
148+
seed: u64,
149+
) -> (Pubkey, u8) {
150+
Pubkey::find_program_address(
151+
&[
152+
EPHEMERAL_STAKE_SEED_PREFIX,
153+
stake_pool_address.as_ref(),
154+
&seed.to_le_bytes(),
155+
],
156+
program_id,
157+
)
158+
}
159+
141160
solana_program::declare_id!("SPoo1Ku8WFXoNDMHPsrGSTSG1Y47rzgn41SLUNakuHy");
142161

143162
#[cfg(test)]

0 commit comments

Comments
 (0)