stake: Advance `credits_observed` on activation epoch (#19309)
* stake: Advance `credits_observed` on activation epoch * Add test for merging stakes just after activation
This commit is contained in:
parent
5e25ee5ebe
commit
2c3bdedea3
|
@ -2,7 +2,8 @@
|
||||||
use {
|
use {
|
||||||
assert_matches::assert_matches,
|
assert_matches::assert_matches,
|
||||||
bincode::deserialize,
|
bincode::deserialize,
|
||||||
solana_program_test::{processor, ProgramTest, ProgramTestError},
|
solana_banks_client::BanksClient,
|
||||||
|
solana_program_test::{processor, ProgramTest, ProgramTestContext, ProgramTestError},
|
||||||
solana_sdk::{
|
solana_sdk::{
|
||||||
account_info::{next_account_info, AccountInfo},
|
account_info::{next_account_info, AccountInfo},
|
||||||
clock::Clock,
|
clock::Clock,
|
||||||
|
@ -34,6 +35,75 @@ use {
|
||||||
// Use a big number to be sure that we get the right error
|
// Use a big number to be sure that we get the right error
|
||||||
const WRONG_SLOT_ERROR: u32 = 123456;
|
const WRONG_SLOT_ERROR: u32 = 123456;
|
||||||
|
|
||||||
|
async fn setup_stake(
|
||||||
|
context: &mut ProgramTestContext,
|
||||||
|
user: &Keypair,
|
||||||
|
vote_address: &Pubkey,
|
||||||
|
stake_lamports: u64,
|
||||||
|
) -> Pubkey {
|
||||||
|
let stake_keypair = Keypair::new();
|
||||||
|
let transaction = Transaction::new_signed_with_payer(
|
||||||
|
&stake_instruction::create_account_and_delegate_stake(
|
||||||
|
&context.payer.pubkey(),
|
||||||
|
&stake_keypair.pubkey(),
|
||||||
|
vote_address,
|
||||||
|
&Authorized::auto(&user.pubkey()),
|
||||||
|
&Lockup::default(),
|
||||||
|
stake_lamports,
|
||||||
|
),
|
||||||
|
Some(&context.payer.pubkey()),
|
||||||
|
&vec![&context.payer, &stake_keypair, user],
|
||||||
|
context.last_blockhash,
|
||||||
|
);
|
||||||
|
context
|
||||||
|
.banks_client
|
||||||
|
.process_transaction(transaction)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
stake_keypair.pubkey()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn setup_vote(context: &mut ProgramTestContext) -> Pubkey {
|
||||||
|
// warp once to make sure stake config doesn't get rent-collected
|
||||||
|
context.warp_to_slot(100).unwrap();
|
||||||
|
let mut instructions = vec![];
|
||||||
|
let validator_keypair = Keypair::new();
|
||||||
|
instructions.push(system_instruction::create_account(
|
||||||
|
&context.payer.pubkey(),
|
||||||
|
&validator_keypair.pubkey(),
|
||||||
|
42,
|
||||||
|
0,
|
||||||
|
&system_program::id(),
|
||||||
|
));
|
||||||
|
let vote_lamports = Rent::default().minimum_balance(VoteState::size_of());
|
||||||
|
let vote_keypair = Keypair::new();
|
||||||
|
let user_keypair = Keypair::new();
|
||||||
|
instructions.append(&mut vote_instruction::create_account(
|
||||||
|
&context.payer.pubkey(),
|
||||||
|
&vote_keypair.pubkey(),
|
||||||
|
&VoteInit {
|
||||||
|
node_pubkey: validator_keypair.pubkey(),
|
||||||
|
authorized_voter: user_keypair.pubkey(),
|
||||||
|
..VoteInit::default()
|
||||||
|
},
|
||||||
|
vote_lamports,
|
||||||
|
));
|
||||||
|
|
||||||
|
let transaction = Transaction::new_signed_with_payer(
|
||||||
|
&instructions,
|
||||||
|
Some(&context.payer.pubkey()),
|
||||||
|
&vec![&context.payer, &validator_keypair, &vote_keypair],
|
||||||
|
context.last_blockhash,
|
||||||
|
);
|
||||||
|
context
|
||||||
|
.banks_client
|
||||||
|
.process_transaction(transaction)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vote_keypair.pubkey()
|
||||||
|
}
|
||||||
|
|
||||||
fn process_instruction(
|
fn process_instruction(
|
||||||
_program_id: &Pubkey,
|
_program_id: &Pubkey,
|
||||||
accounts: &[AccountInfo],
|
accounts: &[AccountInfo],
|
||||||
|
@ -166,63 +236,17 @@ async fn rent_collected_from_warp() {
|
||||||
async fn stake_rewards_from_warp() {
|
async fn stake_rewards_from_warp() {
|
||||||
// Initialize and start the test network
|
// Initialize and start the test network
|
||||||
let program_test = ProgramTest::default();
|
let program_test = ProgramTest::default();
|
||||||
|
|
||||||
let mut context = program_test.start_with_context().await;
|
let mut context = program_test.start_with_context().await;
|
||||||
// warp once to make sure stake config doesn't get rent-collected
|
let vote_address = setup_vote(&mut context).await;
|
||||||
context.warp_to_slot(100).unwrap();
|
|
||||||
let mut instructions = vec![];
|
|
||||||
let validator_keypair = Keypair::new();
|
|
||||||
instructions.push(system_instruction::create_account(
|
|
||||||
&context.payer.pubkey(),
|
|
||||||
&validator_keypair.pubkey(),
|
|
||||||
42,
|
|
||||||
0,
|
|
||||||
&system_program::id(),
|
|
||||||
));
|
|
||||||
let vote_lamports = Rent::default().minimum_balance(VoteState::size_of());
|
|
||||||
let vote_keypair = Keypair::new();
|
|
||||||
let user_keypair = Keypair::new();
|
|
||||||
instructions.append(&mut vote_instruction::create_account(
|
|
||||||
&context.payer.pubkey(),
|
|
||||||
&vote_keypair.pubkey(),
|
|
||||||
&VoteInit {
|
|
||||||
node_pubkey: validator_keypair.pubkey(),
|
|
||||||
authorized_voter: user_keypair.pubkey(),
|
|
||||||
..VoteInit::default()
|
|
||||||
},
|
|
||||||
vote_lamports,
|
|
||||||
));
|
|
||||||
|
|
||||||
let stake_keypair = Keypair::new();
|
let user_keypair = Keypair::new();
|
||||||
let stake_lamports = 1_000_000_000_000;
|
let stake_lamports = 1_000_000_000_000;
|
||||||
instructions.append(&mut stake_instruction::create_account_and_delegate_stake(
|
let stake_address =
|
||||||
&context.payer.pubkey(),
|
setup_stake(&mut context, &user_keypair, &vote_address, stake_lamports).await;
|
||||||
&stake_keypair.pubkey(),
|
|
||||||
&vote_keypair.pubkey(),
|
|
||||||
&Authorized::auto(&user_keypair.pubkey()),
|
|
||||||
&Lockup::default(),
|
|
||||||
stake_lamports,
|
|
||||||
));
|
|
||||||
let transaction = Transaction::new_signed_with_payer(
|
|
||||||
&instructions,
|
|
||||||
Some(&context.payer.pubkey()),
|
|
||||||
&vec![
|
|
||||||
&context.payer,
|
|
||||||
&validator_keypair,
|
|
||||||
&vote_keypair,
|
|
||||||
&stake_keypair,
|
|
||||||
&user_keypair,
|
|
||||||
],
|
|
||||||
context.last_blockhash,
|
|
||||||
);
|
|
||||||
context
|
|
||||||
.banks_client
|
|
||||||
.process_transaction(transaction)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let account = context
|
let account = context
|
||||||
.banks_client
|
.banks_client
|
||||||
.get_account(stake_keypair.pubkey())
|
.get_account(stake_address)
|
||||||
.await
|
.await
|
||||||
.expect("account exists")
|
.expect("account exists")
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -233,13 +257,13 @@ async fn stake_rewards_from_warp() {
|
||||||
context.warp_to_slot(first_normal_slot).unwrap();
|
context.warp_to_slot(first_normal_slot).unwrap();
|
||||||
let account = context
|
let account = context
|
||||||
.banks_client
|
.banks_client
|
||||||
.get_account(stake_keypair.pubkey())
|
.get_account(stake_address)
|
||||||
.await
|
.await
|
||||||
.expect("account exists")
|
.expect("account exists")
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(account.lamports, stake_lamports);
|
assert_eq!(account.lamports, stake_lamports);
|
||||||
|
|
||||||
context.increment_vote_account_credits(&vote_keypair.pubkey(), 100);
|
context.increment_vote_account_credits(&vote_address, 100);
|
||||||
|
|
||||||
// go forward and see that rewards have been distributed
|
// go forward and see that rewards have been distributed
|
||||||
let slots_per_epoch = context.genesis_config().epoch_schedule.slots_per_epoch;
|
let slots_per_epoch = context.genesis_config().epoch_schedule.slots_per_epoch;
|
||||||
|
@ -249,7 +273,7 @@ async fn stake_rewards_from_warp() {
|
||||||
|
|
||||||
let account = context
|
let account = context
|
||||||
.banks_client
|
.banks_client
|
||||||
.get_account(stake_keypair.pubkey())
|
.get_account(stake_address)
|
||||||
.await
|
.await
|
||||||
.expect("account exists")
|
.expect("account exists")
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -281,3 +305,112 @@ async fn stake_rewards_from_warp() {
|
||||||
(_, 0, 0)
|
(_, 0, 0)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn check_credits_observed(
|
||||||
|
banks_client: &mut BanksClient,
|
||||||
|
stake_address: Pubkey,
|
||||||
|
expected_credits: u64,
|
||||||
|
) {
|
||||||
|
let stake_account = banks_client
|
||||||
|
.get_account(stake_address)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
let stake_state: StakeState = deserialize(&stake_account.data).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
stake_state.stake().unwrap().credits_observed,
|
||||||
|
expected_credits
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn stake_merge_immediately_after_activation() {
|
||||||
|
let program_test = ProgramTest::default();
|
||||||
|
let mut context = program_test.start_with_context().await;
|
||||||
|
let vote_address = setup_vote(&mut context).await;
|
||||||
|
context.increment_vote_account_credits(&vote_address, 100);
|
||||||
|
|
||||||
|
let first_normal_slot = context.genesis_config().epoch_schedule.first_normal_slot;
|
||||||
|
let slots_per_epoch = context.genesis_config().epoch_schedule.slots_per_epoch;
|
||||||
|
let mut current_slot = first_normal_slot + slots_per_epoch;
|
||||||
|
context.warp_to_slot(current_slot).unwrap();
|
||||||
|
|
||||||
|
// this is annoying, but if no stake has earned rewards, the bank won't
|
||||||
|
// iterate through the stakes at all, which means we can only test the
|
||||||
|
// behavior of advancing credits observed if another stake is earning rewards
|
||||||
|
|
||||||
|
// make a base stake which receives rewards
|
||||||
|
let user_keypair = Keypair::new();
|
||||||
|
let stake_lamports = 1_000_000_000_000;
|
||||||
|
let base_stake_address =
|
||||||
|
setup_stake(&mut context, &user_keypair, &vote_address, stake_lamports).await;
|
||||||
|
check_credits_observed(&mut context.banks_client, base_stake_address, 100).await;
|
||||||
|
context.increment_vote_account_credits(&vote_address, 100);
|
||||||
|
|
||||||
|
current_slot += slots_per_epoch;
|
||||||
|
context.warp_to_slot(current_slot).unwrap();
|
||||||
|
|
||||||
|
// make another stake which will just have its credits observed advanced
|
||||||
|
let absorbed_stake_address =
|
||||||
|
setup_stake(&mut context, &user_keypair, &vote_address, stake_lamports).await;
|
||||||
|
// the new stake is at the right value
|
||||||
|
check_credits_observed(&mut context.banks_client, absorbed_stake_address, 200).await;
|
||||||
|
// the base stake hasn't been moved forward because no rewards were earned
|
||||||
|
check_credits_observed(&mut context.banks_client, base_stake_address, 100).await;
|
||||||
|
|
||||||
|
context.increment_vote_account_credits(&vote_address, 100);
|
||||||
|
current_slot += slots_per_epoch;
|
||||||
|
context.warp_to_slot(current_slot).unwrap();
|
||||||
|
|
||||||
|
// check that base stake has earned rewards and credits moved forward
|
||||||
|
let stake_account = context
|
||||||
|
.banks_client
|
||||||
|
.get_account(base_stake_address)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
let stake_state: StakeState = deserialize(&stake_account.data).unwrap();
|
||||||
|
assert_eq!(stake_state.stake().unwrap().credits_observed, 300);
|
||||||
|
assert!(stake_account.lamports > stake_lamports);
|
||||||
|
|
||||||
|
// check that new stake hasn't earned rewards, but that credits_observed have been advanced
|
||||||
|
let stake_account = context
|
||||||
|
.banks_client
|
||||||
|
.get_account(absorbed_stake_address)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
let stake_state: StakeState = deserialize(&stake_account.data).unwrap();
|
||||||
|
assert_eq!(stake_state.stake().unwrap().credits_observed, 300);
|
||||||
|
assert_eq!(stake_account.lamports, stake_lamports);
|
||||||
|
|
||||||
|
// sanity-check that the activation epoch was actually last epoch
|
||||||
|
let clock_account = context
|
||||||
|
.banks_client
|
||||||
|
.get_account(clock::id())
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
let clock: Clock = deserialize(&clock_account.data).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
clock.epoch,
|
||||||
|
stake_state.delegation().unwrap().activation_epoch + 1
|
||||||
|
);
|
||||||
|
|
||||||
|
// sanity-check that it's possible to merge the just-activated stake with the older stake!
|
||||||
|
let transaction = Transaction::new_signed_with_payer(
|
||||||
|
&stake_instruction::merge(
|
||||||
|
&base_stake_address,
|
||||||
|
&absorbed_stake_address,
|
||||||
|
&user_keypair.pubkey(),
|
||||||
|
),
|
||||||
|
Some(&context.payer.pubkey()),
|
||||||
|
&vec![&context.payer, &user_keypair],
|
||||||
|
context.last_blockhash,
|
||||||
|
);
|
||||||
|
context
|
||||||
|
.banks_client
|
||||||
|
.process_transaction(transaction)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
|
@ -156,11 +156,13 @@ pub struct PointValue {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn redeem_stake_rewards(
|
fn redeem_stake_rewards(
|
||||||
|
rewarded_epoch: Epoch,
|
||||||
stake: &mut Stake,
|
stake: &mut Stake,
|
||||||
point_value: &PointValue,
|
point_value: &PointValue,
|
||||||
vote_state: &VoteState,
|
vote_state: &VoteState,
|
||||||
stake_history: Option<&StakeHistory>,
|
stake_history: Option<&StakeHistory>,
|
||||||
inflation_point_calc_tracer: &mut Option<impl FnMut(&InflationPointCalculationEvent)>,
|
inflation_point_calc_tracer: &mut Option<impl FnMut(&InflationPointCalculationEvent)>,
|
||||||
|
fix_activating_credits_observed: bool,
|
||||||
) -> Option<(u64, u64)> {
|
) -> Option<(u64, u64)> {
|
||||||
if let Some(inflation_point_calc_tracer) = inflation_point_calc_tracer {
|
if let Some(inflation_point_calc_tracer) = inflation_point_calc_tracer {
|
||||||
inflation_point_calc_tracer(&InflationPointCalculationEvent::CreditsObserved(
|
inflation_point_calc_tracer(&InflationPointCalculationEvent::CreditsObserved(
|
||||||
|
@ -169,11 +171,13 @@ fn redeem_stake_rewards(
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
calculate_stake_rewards(
|
calculate_stake_rewards(
|
||||||
|
rewarded_epoch,
|
||||||
stake,
|
stake,
|
||||||
point_value,
|
point_value,
|
||||||
vote_state,
|
vote_state,
|
||||||
stake_history,
|
stake_history,
|
||||||
inflation_point_calc_tracer,
|
inflation_point_calc_tracer,
|
||||||
|
fix_activating_credits_observed,
|
||||||
)
|
)
|
||||||
.map(|(stakers_reward, voters_reward, credits_observed)| {
|
.map(|(stakers_reward, voters_reward, credits_observed)| {
|
||||||
if let Some(inflation_point_calc_tracer) = inflation_point_calc_tracer {
|
if let Some(inflation_point_calc_tracer) = inflation_point_calc_tracer {
|
||||||
|
@ -270,11 +274,13 @@ fn calculate_stake_points_and_credits(
|
||||||
/// * new value for credits_observed in the stake
|
/// * new value for credits_observed in the stake
|
||||||
/// returns None if there's no payout or if any deserved payout is < 1 lamport
|
/// returns None if there's no payout or if any deserved payout is < 1 lamport
|
||||||
fn calculate_stake_rewards(
|
fn calculate_stake_rewards(
|
||||||
|
rewarded_epoch: Epoch,
|
||||||
stake: &Stake,
|
stake: &Stake,
|
||||||
point_value: &PointValue,
|
point_value: &PointValue,
|
||||||
vote_state: &VoteState,
|
vote_state: &VoteState,
|
||||||
stake_history: Option<&StakeHistory>,
|
stake_history: Option<&StakeHistory>,
|
||||||
inflation_point_calc_tracer: &mut Option<impl FnMut(&InflationPointCalculationEvent)>,
|
inflation_point_calc_tracer: &mut Option<impl FnMut(&InflationPointCalculationEvent)>,
|
||||||
|
fix_activating_credits_observed: bool,
|
||||||
) -> Option<(u64, u64, u64)> {
|
) -> Option<(u64, u64, u64)> {
|
||||||
let (points, credits_observed) = calculate_stake_points_and_credits(
|
let (points, credits_observed) = calculate_stake_points_and_credits(
|
||||||
stake,
|
stake,
|
||||||
|
@ -284,7 +290,10 @@ fn calculate_stake_rewards(
|
||||||
);
|
);
|
||||||
|
|
||||||
// Drive credits_observed forward unconditionally when rewards are disabled
|
// Drive credits_observed forward unconditionally when rewards are disabled
|
||||||
if point_value.rewards == 0 {
|
// or when this is the stake's activation epoch
|
||||||
|
if point_value.rewards == 0
|
||||||
|
|| (fix_activating_credits_observed && stake.delegation.activation_epoch == rewarded_epoch)
|
||||||
|
{
|
||||||
return Some((0, 0, credits_observed));
|
return Some((0, 0, credits_observed));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1098,6 +1107,7 @@ pub fn redeem_rewards(
|
||||||
point_value: &PointValue,
|
point_value: &PointValue,
|
||||||
stake_history: Option<&StakeHistory>,
|
stake_history: Option<&StakeHistory>,
|
||||||
inflation_point_calc_tracer: &mut Option<impl FnMut(&InflationPointCalculationEvent)>,
|
inflation_point_calc_tracer: &mut Option<impl FnMut(&InflationPointCalculationEvent)>,
|
||||||
|
fix_activating_credits_observed: bool,
|
||||||
) -> Result<(u64, u64), InstructionError> {
|
) -> Result<(u64, u64), InstructionError> {
|
||||||
if let StakeState::Stake(meta, mut stake) = stake_account.state()? {
|
if let StakeState::Stake(meta, mut stake) = stake_account.state()? {
|
||||||
if let Some(inflation_point_calc_tracer) = inflation_point_calc_tracer {
|
if let Some(inflation_point_calc_tracer) = inflation_point_calc_tracer {
|
||||||
|
@ -1115,11 +1125,13 @@ pub fn redeem_rewards(
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((stakers_reward, voters_reward)) = redeem_stake_rewards(
|
if let Some((stakers_reward, voters_reward)) = redeem_stake_rewards(
|
||||||
|
rewarded_epoch,
|
||||||
&mut stake,
|
&mut stake,
|
||||||
point_value,
|
point_value,
|
||||||
vote_state,
|
vote_state,
|
||||||
stake_history,
|
stake_history,
|
||||||
inflation_point_calc_tracer,
|
inflation_point_calc_tracer,
|
||||||
|
fix_activating_credits_observed,
|
||||||
) {
|
) {
|
||||||
stake_account.checked_add_lamports(stakers_reward)?;
|
stake_account.checked_add_lamports(stakers_reward)?;
|
||||||
vote_account.checked_add_lamports(voters_reward)?;
|
vote_account.checked_add_lamports(voters_reward)?;
|
||||||
|
@ -3379,6 +3391,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
None,
|
None,
|
||||||
redeem_stake_rewards(
|
redeem_stake_rewards(
|
||||||
|
0,
|
||||||
&mut stake,
|
&mut stake,
|
||||||
&PointValue {
|
&PointValue {
|
||||||
rewards: 1_000_000_000,
|
rewards: 1_000_000_000,
|
||||||
|
@ -3387,6 +3400,7 @@ mod tests {
|
||||||
&vote_state,
|
&vote_state,
|
||||||
None,
|
None,
|
||||||
&mut null_tracer(),
|
&mut null_tracer(),
|
||||||
|
true,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -3398,6 +3412,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some((stake_lamports * 2, 0)),
|
Some((stake_lamports * 2, 0)),
|
||||||
redeem_stake_rewards(
|
redeem_stake_rewards(
|
||||||
|
0,
|
||||||
&mut stake,
|
&mut stake,
|
||||||
&PointValue {
|
&PointValue {
|
||||||
rewards: 1,
|
rewards: 1,
|
||||||
|
@ -3406,6 +3421,7 @@ mod tests {
|
||||||
&vote_state,
|
&vote_state,
|
||||||
None,
|
None,
|
||||||
&mut null_tracer(),
|
&mut null_tracer(),
|
||||||
|
true,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -3434,6 +3450,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
None,
|
None,
|
||||||
calculate_stake_rewards(
|
calculate_stake_rewards(
|
||||||
|
0,
|
||||||
&stake,
|
&stake,
|
||||||
&PointValue {
|
&PointValue {
|
||||||
rewards: 1_000_000_000,
|
rewards: 1_000_000_000,
|
||||||
|
@ -3442,6 +3459,7 @@ mod tests {
|
||||||
&vote_state,
|
&vote_state,
|
||||||
None,
|
None,
|
||||||
&mut null_tracer(),
|
&mut null_tracer(),
|
||||||
|
true,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -3476,6 +3494,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
None,
|
None,
|
||||||
calculate_stake_rewards(
|
calculate_stake_rewards(
|
||||||
|
0,
|
||||||
&stake,
|
&stake,
|
||||||
&PointValue {
|
&PointValue {
|
||||||
rewards: 1_000_000_000,
|
rewards: 1_000_000_000,
|
||||||
|
@ -3484,6 +3503,7 @@ mod tests {
|
||||||
&vote_state,
|
&vote_state,
|
||||||
None,
|
None,
|
||||||
&mut null_tracer(),
|
&mut null_tracer(),
|
||||||
|
true,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -3495,6 +3515,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some((stake.delegation.stake * 2, 0, 2)),
|
Some((stake.delegation.stake * 2, 0, 2)),
|
||||||
calculate_stake_rewards(
|
calculate_stake_rewards(
|
||||||
|
0,
|
||||||
&stake,
|
&stake,
|
||||||
&PointValue {
|
&PointValue {
|
||||||
rewards: 2,
|
rewards: 2,
|
||||||
|
@ -3503,6 +3524,7 @@ mod tests {
|
||||||
&vote_state,
|
&vote_state,
|
||||||
None,
|
None,
|
||||||
&mut null_tracer(),
|
&mut null_tracer(),
|
||||||
|
true,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -3511,6 +3533,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some((stake.delegation.stake, 0, 2)),
|
Some((stake.delegation.stake, 0, 2)),
|
||||||
calculate_stake_rewards(
|
calculate_stake_rewards(
|
||||||
|
0,
|
||||||
&stake,
|
&stake,
|
||||||
&PointValue {
|
&PointValue {
|
||||||
rewards: 1,
|
rewards: 1,
|
||||||
|
@ -3519,6 +3542,7 @@ mod tests {
|
||||||
&vote_state,
|
&vote_state,
|
||||||
None,
|
None,
|
||||||
&mut null_tracer(),
|
&mut null_tracer(),
|
||||||
|
true,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -3530,6 +3554,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some((stake.delegation.stake, 0, 3)),
|
Some((stake.delegation.stake, 0, 3)),
|
||||||
calculate_stake_rewards(
|
calculate_stake_rewards(
|
||||||
|
1,
|
||||||
&stake,
|
&stake,
|
||||||
&PointValue {
|
&PointValue {
|
||||||
rewards: 2,
|
rewards: 2,
|
||||||
|
@ -3538,6 +3563,7 @@ mod tests {
|
||||||
&vote_state,
|
&vote_state,
|
||||||
None,
|
None,
|
||||||
&mut null_tracer(),
|
&mut null_tracer(),
|
||||||
|
true,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -3547,6 +3573,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some((stake.delegation.stake * 2, 0, 4)),
|
Some((stake.delegation.stake * 2, 0, 4)),
|
||||||
calculate_stake_rewards(
|
calculate_stake_rewards(
|
||||||
|
2,
|
||||||
&stake,
|
&stake,
|
||||||
&PointValue {
|
&PointValue {
|
||||||
rewards: 2,
|
rewards: 2,
|
||||||
|
@ -3555,6 +3582,7 @@ mod tests {
|
||||||
&vote_state,
|
&vote_state,
|
||||||
None,
|
None,
|
||||||
&mut null_tracer(),
|
&mut null_tracer(),
|
||||||
|
true,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -3570,6 +3598,7 @@ mod tests {
|
||||||
4
|
4
|
||||||
)),
|
)),
|
||||||
calculate_stake_rewards(
|
calculate_stake_rewards(
|
||||||
|
2,
|
||||||
&stake,
|
&stake,
|
||||||
&PointValue {
|
&PointValue {
|
||||||
rewards: 4,
|
rewards: 4,
|
||||||
|
@ -3578,6 +3607,7 @@ mod tests {
|
||||||
&vote_state,
|
&vote_state,
|
||||||
None,
|
None,
|
||||||
&mut null_tracer(),
|
&mut null_tracer(),
|
||||||
|
true,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -3587,6 +3617,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
None, // would be Some((0, 2 * 1 + 1 * 2, 4)),
|
None, // would be Some((0, 2 * 1 + 1 * 2, 4)),
|
||||||
calculate_stake_rewards(
|
calculate_stake_rewards(
|
||||||
|
2,
|
||||||
&stake,
|
&stake,
|
||||||
&PointValue {
|
&PointValue {
|
||||||
rewards: 4,
|
rewards: 4,
|
||||||
|
@ -3595,12 +3626,14 @@ mod tests {
|
||||||
&vote_state,
|
&vote_state,
|
||||||
None,
|
None,
|
||||||
&mut null_tracer(),
|
&mut null_tracer(),
|
||||||
|
true,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
vote_state.commission = 99;
|
vote_state.commission = 99;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
None, // would be Some((0, 2 * 1 + 1 * 2, 4)),
|
None, // would be Some((0, 2 * 1 + 1 * 2, 4)),
|
||||||
calculate_stake_rewards(
|
calculate_stake_rewards(
|
||||||
|
2,
|
||||||
&stake,
|
&stake,
|
||||||
&PointValue {
|
&PointValue {
|
||||||
rewards: 4,
|
rewards: 4,
|
||||||
|
@ -3609,6 +3642,7 @@ mod tests {
|
||||||
&vote_state,
|
&vote_state,
|
||||||
None,
|
None,
|
||||||
&mut null_tracer(),
|
&mut null_tracer(),
|
||||||
|
true,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -3618,6 +3652,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some((0, 0, 4)),
|
Some((0, 0, 4)),
|
||||||
calculate_stake_rewards(
|
calculate_stake_rewards(
|
||||||
|
2,
|
||||||
&stake,
|
&stake,
|
||||||
&PointValue {
|
&PointValue {
|
||||||
rewards: 0,
|
rewards: 0,
|
||||||
|
@ -3626,6 +3661,7 @@ mod tests {
|
||||||
&vote_state,
|
&vote_state,
|
||||||
None,
|
None,
|
||||||
&mut null_tracer(),
|
&mut null_tracer(),
|
||||||
|
true,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -3635,6 +3671,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some((0, 0, 4)),
|
Some((0, 0, 4)),
|
||||||
calculate_stake_rewards(
|
calculate_stake_rewards(
|
||||||
|
2,
|
||||||
&stake,
|
&stake,
|
||||||
&PointValue {
|
&PointValue {
|
||||||
rewards: 0,
|
rewards: 0,
|
||||||
|
@ -3643,6 +3680,7 @@ mod tests {
|
||||||
&vote_state,
|
&vote_state,
|
||||||
None,
|
None,
|
||||||
&mut null_tracer(),
|
&mut null_tracer(),
|
||||||
|
true,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -3651,6 +3689,50 @@ mod tests {
|
||||||
(0, 4),
|
(0, 4),
|
||||||
calculate_stake_points_and_credits(&stake, &vote_state, None, &mut null_tracer())
|
calculate_stake_points_and_credits(&stake, &vote_state, None, &mut null_tracer())
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// get rewards and credits observed when not the activation epoch
|
||||||
|
vote_state.commission = 0;
|
||||||
|
stake.credits_observed = 3;
|
||||||
|
stake.delegation.activation_epoch = 1;
|
||||||
|
assert_eq!(
|
||||||
|
Some((
|
||||||
|
stake.delegation.stake, // epoch 2
|
||||||
|
0,
|
||||||
|
4
|
||||||
|
)),
|
||||||
|
calculate_stake_rewards(
|
||||||
|
2,
|
||||||
|
&stake,
|
||||||
|
&PointValue {
|
||||||
|
rewards: 1,
|
||||||
|
points: 1
|
||||||
|
},
|
||||||
|
&vote_state,
|
||||||
|
None,
|
||||||
|
&mut null_tracer(),
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// credits_observed is moved forward for the stake's activation epoch,
|
||||||
|
// and no rewards are perceived
|
||||||
|
stake.delegation.activation_epoch = 2;
|
||||||
|
stake.credits_observed = 3;
|
||||||
|
assert_eq!(
|
||||||
|
Some((0, 0, 4)),
|
||||||
|
calculate_stake_rewards(
|
||||||
|
2,
|
||||||
|
&stake,
|
||||||
|
&PointValue {
|
||||||
|
rewards: 1,
|
||||||
|
points: 1
|
||||||
|
},
|
||||||
|
&vote_state,
|
||||||
|
None,
|
||||||
|
&mut null_tracer(),
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -1940,8 +1940,12 @@ impl Bank {
|
||||||
|
|
||||||
let old_vote_balance_and_staked = self.stakes.read().unwrap().vote_balance_and_staked();
|
let old_vote_balance_and_staked = self.stakes.read().unwrap().vote_balance_and_staked();
|
||||||
|
|
||||||
let validator_point_value =
|
let validator_point_value = self.pay_validator_rewards(
|
||||||
self.pay_validator_rewards(prev_epoch, validator_rewards, reward_calc_tracer);
|
prev_epoch,
|
||||||
|
validator_rewards,
|
||||||
|
reward_calc_tracer,
|
||||||
|
self.stake_program_advance_activating_credits_observed(),
|
||||||
|
);
|
||||||
|
|
||||||
if !self
|
if !self
|
||||||
.feature_set
|
.feature_set
|
||||||
|
@ -2078,6 +2082,7 @@ impl Bank {
|
||||||
rewarded_epoch: Epoch,
|
rewarded_epoch: Epoch,
|
||||||
rewards: u64,
|
rewards: u64,
|
||||||
reward_calc_tracer: &mut Option<impl FnMut(&RewardCalculationEvent)>,
|
reward_calc_tracer: &mut Option<impl FnMut(&RewardCalculationEvent)>,
|
||||||
|
fix_activating_credits_observed: bool,
|
||||||
) -> f64 {
|
) -> f64 {
|
||||||
let stake_history = self.stakes.read().unwrap().history().clone();
|
let stake_history = self.stakes.read().unwrap().history().clone();
|
||||||
|
|
||||||
|
@ -2136,6 +2141,7 @@ impl Bank {
|
||||||
&point_value,
|
&point_value,
|
||||||
Some(&stake_history),
|
Some(&stake_history),
|
||||||
&mut reward_calc_tracer.as_mut(),
|
&mut reward_calc_tracer.as_mut(),
|
||||||
|
fix_activating_credits_observed,
|
||||||
);
|
);
|
||||||
if let Ok((stakers_reward, _voters_reward)) = redeemed {
|
if let Ok((stakers_reward, _voters_reward)) = redeemed {
|
||||||
self.store_account(stake_pubkey, stake_account);
|
self.store_account(stake_pubkey, stake_account);
|
||||||
|
@ -5365,6 +5371,11 @@ impl Bank {
|
||||||
.is_active(&feature_set::versioned_tx_message_enabled::id())
|
.is_active(&feature_set::versioned_tx_message_enabled::id())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn stake_program_advance_activating_credits_observed(&self) -> bool {
|
||||||
|
self.feature_set
|
||||||
|
.is_active(&feature_set::stake_program_advance_activating_credits_observed::id())
|
||||||
|
}
|
||||||
|
|
||||||
// Check if the wallclock time from bank creation to now has exceeded the allotted
|
// Check if the wallclock time from bank creation to now has exceeded the allotted
|
||||||
// time for transaction processing
|
// time for transaction processing
|
||||||
pub fn should_bank_still_be_processing_txs(
|
pub fn should_bank_still_be_processing_txs(
|
||||||
|
|
|
@ -187,6 +187,10 @@ pub mod close_upgradeable_program_accounts {
|
||||||
solana_sdk::declare_id!("EQMtCuSAkMVF9ZdhGuABtgvyXJLtSRF5AQKv1RNsrhj7");
|
solana_sdk::declare_id!("EQMtCuSAkMVF9ZdhGuABtgvyXJLtSRF5AQKv1RNsrhj7");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod stake_program_advance_activating_credits_observed {
|
||||||
|
solana_sdk::declare_id!("SAdVFw3RZvzbo6DvySbSdBnHN4gkzSTH9dSxesyKKPj");
|
||||||
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
/// Map of feature identifiers to user-visible description
|
/// Map of feature identifiers to user-visible description
|
||||||
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
|
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
|
||||||
|
@ -229,6 +233,7 @@ lazy_static! {
|
||||||
(libsecp256k1_fail_on_bad_count::id(), "Fail libsec256k1_verify if count appears wrong"),
|
(libsecp256k1_fail_on_bad_count::id(), "Fail libsec256k1_verify if count appears wrong"),
|
||||||
(instructions_sysvar_owned_by_sysvar::id(), "fix owner for instructions sysvar"),
|
(instructions_sysvar_owned_by_sysvar::id(), "fix owner for instructions sysvar"),
|
||||||
(close_upgradeable_program_accounts::id(), "enable closing upgradeable program accounts"),
|
(close_upgradeable_program_accounts::id(), "enable closing upgradeable program accounts"),
|
||||||
|
(stake_program_advance_activating_credits_observed::id(), "Enable advancing credits observed for activation epoch #19309"),
|
||||||
/*************** ADD NEW FEATURES HERE ***************/
|
/*************** ADD NEW FEATURES HERE ***************/
|
||||||
]
|
]
|
||||||
.iter()
|
.iter()
|
||||||
|
|
Loading…
Reference in New Issue