stake-pool: Fixup accounting on increase transient account (#1985)
This commit is contained in:
parent
0b3552b892
commit
ac2f9e09a4
|
@ -105,9 +105,10 @@ fn command_create_pool(
|
||||||
max_validators: u32,
|
max_validators: u32,
|
||||||
stake_pool_keypair: Option<Keypair>,
|
stake_pool_keypair: Option<Keypair>,
|
||||||
mint_keypair: Option<Keypair>,
|
mint_keypair: Option<Keypair>,
|
||||||
|
reserve_keypair: Option<Keypair>,
|
||||||
) -> CommandResult {
|
) -> CommandResult {
|
||||||
let reserve_stake = Keypair::new();
|
let reserve_keypair = reserve_keypair.unwrap_or_else(Keypair::new);
|
||||||
println!("Creating reserve stake {}", reserve_stake.pubkey());
|
println!("Creating reserve stake {}", reserve_keypair.pubkey());
|
||||||
|
|
||||||
let mint_keypair = mint_keypair.unwrap_or_else(Keypair::new);
|
let mint_keypair = mint_keypair.unwrap_or_else(Keypair::new);
|
||||||
println!("Creating mint {}", mint_keypair.pubkey());
|
println!("Creating mint {}", mint_keypair.pubkey());
|
||||||
|
@ -163,13 +164,13 @@ fn command_create_pool(
|
||||||
// Account for the stake pool reserve
|
// Account for the stake pool reserve
|
||||||
system_instruction::create_account(
|
system_instruction::create_account(
|
||||||
&config.fee_payer.pubkey(),
|
&config.fee_payer.pubkey(),
|
||||||
&reserve_stake.pubkey(),
|
&reserve_keypair.pubkey(),
|
||||||
reserve_stake_balance,
|
reserve_stake_balance,
|
||||||
STAKE_STATE_LEN as u64,
|
STAKE_STATE_LEN as u64,
|
||||||
&stake_program::id(),
|
&stake_program::id(),
|
||||||
),
|
),
|
||||||
stake_program::initialize(
|
stake_program::initialize(
|
||||||
&reserve_stake.pubkey(),
|
&reserve_keypair.pubkey(),
|
||||||
&stake_program::Authorized {
|
&stake_program::Authorized {
|
||||||
staker: withdraw_authority,
|
staker: withdraw_authority,
|
||||||
withdrawer: withdraw_authority,
|
withdrawer: withdraw_authority,
|
||||||
|
@ -236,7 +237,7 @@ fn command_create_pool(
|
||||||
&config.manager.pubkey(),
|
&config.manager.pubkey(),
|
||||||
&config.staker.pubkey(),
|
&config.staker.pubkey(),
|
||||||
&validator_list.pubkey(),
|
&validator_list.pubkey(),
|
||||||
&reserve_stake.pubkey(),
|
&reserve_keypair.pubkey(),
|
||||||
&mint_keypair.pubkey(),
|
&mint_keypair.pubkey(),
|
||||||
&pool_fee_account.pubkey(),
|
&pool_fee_account.pubkey(),
|
||||||
&spl_token::id(),
|
&spl_token::id(),
|
||||||
|
@ -259,7 +260,7 @@ fn command_create_pool(
|
||||||
config.fee_payer.as_ref(),
|
config.fee_payer.as_ref(),
|
||||||
&mint_keypair,
|
&mint_keypair,
|
||||||
&pool_fee_account,
|
&pool_fee_account,
|
||||||
&reserve_stake,
|
&reserve_keypair,
|
||||||
];
|
];
|
||||||
unique_signers!(setup_signers);
|
unique_signers!(setup_signers);
|
||||||
setup_transaction.sign(&setup_signers, recent_blockhash);
|
setup_transaction.sign(&setup_signers, recent_blockhash);
|
||||||
|
@ -640,6 +641,13 @@ fn command_list(config: &Config, stake_pool_address: &Pubkey) -> CommandResult {
|
||||||
let pool_mint = get_token_mint(&config.rpc_client, &stake_pool.pool_mint)?;
|
let pool_mint = get_token_mint(&config.rpc_client, &stake_pool.pool_mint)?;
|
||||||
let epoch_info = config.rpc_client.get_epoch_info()?;
|
let epoch_info = config.rpc_client.get_epoch_info()?;
|
||||||
|
|
||||||
|
let reserve_stake = config.rpc_client.get_account(&stake_pool.reserve_stake)?;
|
||||||
|
println!(
|
||||||
|
"Reserve Account: {}\tBalance: {}",
|
||||||
|
stake_pool.reserve_stake,
|
||||||
|
Sol(reserve_stake.lamports),
|
||||||
|
);
|
||||||
|
|
||||||
for validator in validator_list.validators {
|
for validator in validator_list.validators {
|
||||||
println!(
|
println!(
|
||||||
"Validator Vote Account: {}\tBalance: {}\tLast Update Epoch: {}{}",
|
"Validator Vote Account: {}\tBalance: {}\tLast Update Epoch: {}{}",
|
||||||
|
@ -1241,6 +1249,14 @@ fn main() {
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.help("Stake pool mint keypair [default: new keypair]"),
|
.help("Stake pool mint keypair [default: new keypair]"),
|
||||||
)
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("reserve_keypair")
|
||||||
|
.long("reserve-keypair")
|
||||||
|
.validator(is_keypair_or_ask_keyword)
|
||||||
|
.value_name("PATH")
|
||||||
|
.takes_value(true)
|
||||||
|
.help("Stake pool reserve keypair [default: new keypair]"),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
.subcommand(SubCommand::with_name("create-validator-stake")
|
.subcommand(SubCommand::with_name("create-validator-stake")
|
||||||
.about("Create a new stake account to use with the pool. Must be signed by the pool staker.")
|
.about("Create a new stake account to use with the pool. Must be signed by the pool staker.")
|
||||||
|
@ -1710,6 +1726,7 @@ fn main() {
|
||||||
let max_validators = value_t_or_exit!(arg_matches, "max_validators", u32);
|
let max_validators = value_t_or_exit!(arg_matches, "max_validators", u32);
|
||||||
let pool_keypair = keypair_of(arg_matches, "pool_keypair");
|
let pool_keypair = keypair_of(arg_matches, "pool_keypair");
|
||||||
let mint_keypair = keypair_of(arg_matches, "mint_keypair");
|
let mint_keypair = keypair_of(arg_matches, "mint_keypair");
|
||||||
|
let reserve_keypair = keypair_of(arg_matches, "reserve_keypair");
|
||||||
command_create_pool(
|
command_create_pool(
|
||||||
&config,
|
&config,
|
||||||
deposit_authority,
|
deposit_authority,
|
||||||
|
@ -1720,6 +1737,7 @@ fn main() {
|
||||||
max_validators,
|
max_validators,
|
||||||
pool_keypair,
|
pool_keypair,
|
||||||
mint_keypair,
|
mint_keypair,
|
||||||
|
reserve_keypair,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
("create-validator-stake", Some(arg_matches)) => {
|
("create-validator-stake", Some(arg_matches)) => {
|
||||||
|
|
|
@ -162,7 +162,11 @@ pub enum StakePoolInstruction {
|
||||||
/// 10. `[]` Stake Config sysvar
|
/// 10. `[]` Stake Config sysvar
|
||||||
/// 11. `[]` System program
|
/// 11. `[]` System program
|
||||||
/// 12. `[]` Stake program
|
/// 12. `[]` Stake program
|
||||||
/// userdata: amount of lamports to split into the transient stake account
|
/// userdata: amount of lamports to increase on the given validator.
|
||||||
|
/// The actual amount split into the transient stake account is:
|
||||||
|
/// `lamports + stake_rent_exemption`
|
||||||
|
/// The rent-exemption of the stake account is withdrawn back to the reserve
|
||||||
|
/// after it is merged.
|
||||||
IncreaseValidatorStake(u64),
|
IncreaseValidatorStake(u64),
|
||||||
|
|
||||||
/// (Staker only) Set the preferred deposit or withdraw stake account for the
|
/// (Staker only) Set the preferred deposit or withdraw stake account for the
|
||||||
|
|
|
@ -33,7 +33,7 @@ pub const MINIMUM_ACTIVE_STAKE: u64 = LAMPORTS_PER_SOL;
|
||||||
|
|
||||||
/// Maximum amount of validator stake accounts to update per
|
/// Maximum amount of validator stake accounts to update per
|
||||||
/// `UpdateValidatorListBalance` instruction, based on compute limits
|
/// `UpdateValidatorListBalance` instruction, based on compute limits
|
||||||
pub const MAX_VALIDATORS_TO_UPDATE: usize = 10;
|
pub const MAX_VALIDATORS_TO_UPDATE: usize = 9;
|
||||||
|
|
||||||
/// Get the stake amount under consideration when calculating pool token
|
/// Get the stake amount under consideration when calculating pool token
|
||||||
/// conversions
|
/// conversions
|
||||||
|
|
|
@ -1150,19 +1150,21 @@ impl Processor {
|
||||||
}
|
}
|
||||||
|
|
||||||
let stake_rent = rent.minimum_balance(std::mem::size_of::<stake_program::StakeState>());
|
let stake_rent = rent.minimum_balance(std::mem::size_of::<stake_program::StakeState>());
|
||||||
let minimum_lamports = MINIMUM_ACTIVE_STAKE + stake_rent;
|
if lamports < MINIMUM_ACTIVE_STAKE {
|
||||||
if lamports < minimum_lamports {
|
|
||||||
msg!(
|
msg!(
|
||||||
"Need more than {} lamports for transient stake to be rent-exempt and mergeable, {} provided",
|
"Need more than {} lamports for transient stake to be rent-exempt and mergeable, {} provided",
|
||||||
minimum_lamports,
|
MINIMUM_ACTIVE_STAKE,
|
||||||
lamports
|
lamports
|
||||||
);
|
);
|
||||||
return Err(ProgramError::AccountNotRentExempt);
|
return Err(ProgramError::AccountNotRentExempt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// the stake account rent exemption is withdrawn after the merge, so
|
||||||
|
let total_lamports = lamports.saturating_add(stake_rent);
|
||||||
|
|
||||||
if reserve_stake_account_info
|
if reserve_stake_account_info
|
||||||
.lamports()
|
.lamports()
|
||||||
.saturating_sub(lamports)
|
.saturating_sub(total_lamports)
|
||||||
<= stake_rent
|
<= stake_rent
|
||||||
{
|
{
|
||||||
let max_split_amount = reserve_stake_account_info
|
let max_split_amount = reserve_stake_account_info
|
||||||
|
@ -1189,7 +1191,7 @@ impl Processor {
|
||||||
withdraw_authority_info.clone(),
|
withdraw_authority_info.clone(),
|
||||||
AUTHORITY_WITHDRAW,
|
AUTHORITY_WITHDRAW,
|
||||||
stake_pool.withdraw_bump_seed,
|
stake_pool.withdraw_bump_seed,
|
||||||
lamports,
|
total_lamports,
|
||||||
transient_stake_account_info.clone(),
|
transient_stake_account_info.clone(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
@ -1206,7 +1208,7 @@ impl Processor {
|
||||||
stake_pool.withdraw_bump_seed,
|
stake_pool.withdraw_bump_seed,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
validator_list_entry.transient_stake_lamports = lamports;
|
validator_list_entry.transient_stake_lamports = total_lamports;
|
||||||
validator_list.serialize(&mut *validator_list_info.data.borrow_mut())?;
|
validator_list.serialize(&mut *validator_list_info.data.borrow_mut())?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -1404,6 +1406,9 @@ impl Processor {
|
||||||
if stake_program::active_stakes_can_merge(&stake, &validator_stake)
|
if stake_program::active_stakes_can_merge(&stake, &validator_stake)
|
||||||
.is_ok()
|
.is_ok()
|
||||||
{
|
{
|
||||||
|
let additional_lamports = transient_stake_info
|
||||||
|
.lamports()
|
||||||
|
.saturating_sub(stake.delegation.stake);
|
||||||
Self::stake_merge(
|
Self::stake_merge(
|
||||||
stake_pool_info.key,
|
stake_pool_info.key,
|
||||||
transient_stake_info.clone(),
|
transient_stake_info.clone(),
|
||||||
|
@ -1415,6 +1420,23 @@ impl Processor {
|
||||||
stake_history_info.clone(),
|
stake_history_info.clone(),
|
||||||
stake_program_info.clone(),
|
stake_program_info.clone(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
// post merge of two active stakes, withdraw
|
||||||
|
// the extra back to the reserve
|
||||||
|
if additional_lamports > 0 {
|
||||||
|
Self::stake_withdraw(
|
||||||
|
stake_pool_info.key,
|
||||||
|
validator_stake_info.clone(),
|
||||||
|
withdraw_authority_info.clone(),
|
||||||
|
AUTHORITY_WITHDRAW,
|
||||||
|
stake_pool.withdraw_bump_seed,
|
||||||
|
reserve_stake_info.clone(),
|
||||||
|
clock_info.clone(),
|
||||||
|
stake_history_info.clone(),
|
||||||
|
stake_program_info.clone(),
|
||||||
|
additional_lamports,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
msg!("Stake activating or just active, not ready to merge");
|
msg!("Stake activating or just active, not ready to merge");
|
||||||
transient_stake_lamports = account_stake;
|
transient_stake_lamports = account_stake;
|
||||||
|
@ -1436,6 +1458,10 @@ impl Processor {
|
||||||
// Status for validator stake
|
// Status for validator stake
|
||||||
// * active -> do everything
|
// * active -> do everything
|
||||||
// * any other state / not a stake -> error state, but account for transient stake
|
// * any other state / not a stake -> error state, but account for transient stake
|
||||||
|
let validator_stake_state = try_from_slice_unchecked::<stake_program::StakeState>(
|
||||||
|
&validator_stake_info.data.borrow(),
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
match validator_stake_state {
|
match validator_stake_state {
|
||||||
Some(stake_program::StakeState::Stake(_, stake)) => {
|
Some(stake_program::StakeState::Stake(_, stake)) => {
|
||||||
if validator_stake_record.status == StakeStatus::Active {
|
if validator_stake_record.status == StakeStatus::Active {
|
||||||
|
@ -1782,8 +1808,8 @@ impl Processor {
|
||||||
/// Processes [Withdraw](enum.Instruction.html).
|
/// Processes [Withdraw](enum.Instruction.html).
|
||||||
fn process_withdraw(
|
fn process_withdraw(
|
||||||
program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
pool_tokens: u64,
|
|
||||||
accounts: &[AccountInfo],
|
accounts: &[AccountInfo],
|
||||||
|
pool_tokens: u64,
|
||||||
) -> ProgramResult {
|
) -> ProgramResult {
|
||||||
let account_info_iter = &mut accounts.iter();
|
let account_info_iter = &mut accounts.iter();
|
||||||
let stake_pool_info = next_account_info(account_info_iter)?;
|
let stake_pool_info = next_account_info(account_info_iter)?;
|
||||||
|
@ -2126,7 +2152,7 @@ impl Processor {
|
||||||
}
|
}
|
||||||
StakePoolInstruction::Withdraw(amount) => {
|
StakePoolInstruction::Withdraw(amount) => {
|
||||||
msg!("Instruction: Withdraw");
|
msg!("Instruction: Withdraw");
|
||||||
Self::process_withdraw(program_id, amount, accounts)
|
Self::process_withdraw(program_id, accounts, amount)
|
||||||
}
|
}
|
||||||
StakePoolInstruction::SetManager => {
|
StakePoolInstruction::SetManager => {
|
||||||
msg!("Instruction: SetManager");
|
msg!("Instruction: SetManager");
|
||||||
|
|
|
@ -93,8 +93,8 @@ async fn success() {
|
||||||
assert!(transient_account.is_none());
|
assert!(transient_account.is_none());
|
||||||
|
|
||||||
let rent = banks_client.get_rent().await.unwrap();
|
let rent = banks_client.get_rent().await.unwrap();
|
||||||
let lamports = rent.minimum_balance(std::mem::size_of::<stake_program::StakeState>());
|
let stake_rent = rent.minimum_balance(std::mem::size_of::<stake_program::StakeState>());
|
||||||
let reserve_lamports = reserve_lamports - lamports;
|
let increase_amount = reserve_lamports - stake_rent - 1;
|
||||||
let error = stake_pool_accounts
|
let error = stake_pool_accounts
|
||||||
.increase_validator_stake(
|
.increase_validator_stake(
|
||||||
&mut banks_client,
|
&mut banks_client,
|
||||||
|
@ -102,7 +102,7 @@ async fn success() {
|
||||||
&recent_blockhash,
|
&recent_blockhash,
|
||||||
&validator_stake.transient_stake_account,
|
&validator_stake.transient_stake_account,
|
||||||
&validator_stake.vote.pubkey(),
|
&validator_stake.vote.pubkey(),
|
||||||
reserve_lamports,
|
increase_amount,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
assert!(error.is_none());
|
assert!(error.is_none());
|
||||||
|
@ -116,7 +116,7 @@ async fn success() {
|
||||||
let reserve_stake_state =
|
let reserve_stake_state =
|
||||||
deserialize::<stake_program::StakeState>(&reserve_stake_account.data).unwrap();
|
deserialize::<stake_program::StakeState>(&reserve_stake_account.data).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
pre_reserve_stake_account.lamports - reserve_lamports,
|
pre_reserve_stake_account.lamports - increase_amount - stake_rent,
|
||||||
reserve_stake_account.lamports
|
reserve_stake_account.lamports
|
||||||
);
|
);
|
||||||
assert!(reserve_stake_state.delegation().is_none());
|
assert!(reserve_stake_state.delegation().is_none());
|
||||||
|
@ -126,7 +126,10 @@ async fn success() {
|
||||||
get_account(&mut banks_client, &validator_stake.transient_stake_account).await;
|
get_account(&mut banks_client, &validator_stake.transient_stake_account).await;
|
||||||
let transient_stake_state =
|
let transient_stake_state =
|
||||||
deserialize::<stake_program::StakeState>(&transient_stake_account.data).unwrap();
|
deserialize::<stake_program::StakeState>(&transient_stake_account.data).unwrap();
|
||||||
assert_eq!(transient_stake_account.lamports, reserve_lamports);
|
assert_eq!(
|
||||||
|
transient_stake_account.lamports,
|
||||||
|
increase_amount + stake_rent
|
||||||
|
);
|
||||||
assert_ne!(
|
assert_ne!(
|
||||||
transient_stake_state.delegation().unwrap().activation_epoch,
|
transient_stake_state.delegation().unwrap().activation_epoch,
|
||||||
Epoch::MAX
|
Epoch::MAX
|
||||||
|
@ -332,7 +335,7 @@ async fn fail_with_small_lamport_amount() {
|
||||||
) = setup().await;
|
) = setup().await;
|
||||||
|
|
||||||
let rent = banks_client.get_rent().await.unwrap();
|
let rent = banks_client.get_rent().await.unwrap();
|
||||||
let lamports = rent.minimum_balance(std::mem::size_of::<stake_program::StakeState>());
|
let stake_rent = rent.minimum_balance(std::mem::size_of::<stake_program::StakeState>());
|
||||||
|
|
||||||
let error = stake_pool_accounts
|
let error = stake_pool_accounts
|
||||||
.increase_validator_stake(
|
.increase_validator_stake(
|
||||||
|
@ -341,7 +344,7 @@ async fn fail_with_small_lamport_amount() {
|
||||||
&recent_blockhash,
|
&recent_blockhash,
|
||||||
&validator_stake.transient_stake_account,
|
&validator_stake.transient_stake_account,
|
||||||
&validator_stake.vote.pubkey(),
|
&validator_stake.vote.pubkey(),
|
||||||
lamports,
|
stake_rent,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
|
|
@ -381,14 +381,6 @@ async fn merge_into_validator_stake() {
|
||||||
let stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool_info.data).unwrap();
|
let stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool_info.data).unwrap();
|
||||||
assert_eq!(expected_lamports, stake_pool.total_stake_lamports);
|
assert_eq!(expected_lamports, stake_pool.total_stake_lamports);
|
||||||
|
|
||||||
let stake_pool_info = get_account(
|
|
||||||
&mut context.banks_client,
|
|
||||||
&stake_pool_accounts.stake_pool.pubkey(),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
let stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool_info.data).unwrap();
|
|
||||||
assert_eq!(expected_lamports, stake_pool.total_stake_lamports);
|
|
||||||
|
|
||||||
// Warp one more epoch so the stakes activate, ready to merge
|
// Warp one more epoch so the stakes activate, ready to merge
|
||||||
let slots_per_epoch = context.genesis_config().epoch_schedule.slots_per_epoch;
|
let slots_per_epoch = context.genesis_config().epoch_schedule.slots_per_epoch;
|
||||||
slot += slots_per_epoch;
|
slot += slots_per_epoch;
|
||||||
|
|
Loading…
Reference in New Issue