Move vote_state current credits into epoch_credits (#7909)
* Move vote_state current credits into epoch_credits * fixups * fixup
This commit is contained in:
parent
1a2d9b8eed
commit
7e1b380f01
|
@ -2381,7 +2381,13 @@ pub mod tests {
|
||||||
assert_ne!(leader_info.activated_stake, 0);
|
assert_ne!(leader_info.activated_stake, 0);
|
||||||
// Subtract one because the last vote always carries over to the next epoch
|
// Subtract one because the last vote always carries over to the next epoch
|
||||||
let expected_credits = TEST_SLOTS_PER_EPOCH - MAX_LOCKOUT_HISTORY as u64 - 1;
|
let expected_credits = TEST_SLOTS_PER_EPOCH - MAX_LOCKOUT_HISTORY as u64 - 1;
|
||||||
assert_eq!(leader_info.epoch_credits, vec![(0, expected_credits, 0)]);
|
assert_eq!(
|
||||||
|
leader_info.epoch_credits,
|
||||||
|
vec![
|
||||||
|
(0, expected_credits, 0),
|
||||||
|
(1, expected_credits + 1, expected_credits) // one vote in current epoch
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
// Advance bank with no voting
|
// Advance bank with no voting
|
||||||
bank.freeze();
|
bank.freeze();
|
||||||
|
|
|
@ -1933,16 +1933,6 @@ mod tests {
|
||||||
vote_state.increment_credits(0);
|
vote_state.increment_credits(0);
|
||||||
vote_state.increment_credits(0);
|
vote_state.increment_credits(0);
|
||||||
|
|
||||||
// this one can't collect now, no epoch credits have been saved off
|
|
||||||
// even though point value is huuge
|
|
||||||
assert_eq!(
|
|
||||||
None,
|
|
||||||
stake.calculate_rewards(1_000_000_000_000.0, &vote_state, None)
|
|
||||||
);
|
|
||||||
|
|
||||||
// put 1 credit in epoch 1, pushes the 2 above into a redeemable state
|
|
||||||
vote_state.increment_credits(1);
|
|
||||||
|
|
||||||
// this one should be able to collect exactly 2
|
// this one should be able to collect exactly 2
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some((0, stake.delegation.stake * 2, 2)),
|
Some((0, stake.delegation.stake * 2, 2)),
|
||||||
|
@ -1950,33 +1940,40 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
stake.credits_observed = 1;
|
stake.credits_observed = 1;
|
||||||
// this one should be able to collect exactly 1 (only observed one)
|
// this one should be able to collect exactly 1 (already observed one)
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some((0, stake.delegation.stake * 1, 2)),
|
Some((0, stake.delegation.stake * 1, 2)),
|
||||||
stake.calculate_rewards(1.0, &vote_state, None)
|
stake.calculate_rewards(1.0, &vote_state, None)
|
||||||
);
|
);
|
||||||
|
|
||||||
stake.credits_observed = 2;
|
// put 1 credit in epoch 1
|
||||||
// this one should be able to collect none because credits_observed >= credits in a
|
vote_state.increment_credits(1);
|
||||||
// redeemable state (the 2 credits in epoch 0)
|
|
||||||
assert_eq!(None, stake.calculate_rewards(1.0, &vote_state, None));
|
|
||||||
|
|
||||||
// put 1 credit in epoch 2, pushes the 1 for epoch 1 to redeemable
|
stake.credits_observed = 2;
|
||||||
vote_state.increment_credits(2);
|
// this one should be able to collect the one just added
|
||||||
// this one should be able to collect 1 now, one credit by a stake of 1
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some((0, stake.delegation.stake * 1, 3)),
|
Some((0, stake.delegation.stake * 1, 3)),
|
||||||
stake.calculate_rewards(1.0, &vote_state, None)
|
stake.calculate_rewards(1.0, &vote_state, None)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// put 1 credit in epoch 2
|
||||||
|
vote_state.increment_credits(2);
|
||||||
|
// this one should be able to collect 2 now
|
||||||
|
assert_eq!(
|
||||||
|
Some((0, stake.delegation.stake * 2, 4)),
|
||||||
|
stake.calculate_rewards(1.0, &vote_state, None)
|
||||||
|
);
|
||||||
|
|
||||||
stake.credits_observed = 0;
|
stake.credits_observed = 0;
|
||||||
// this one should be able to collect everything from t=0 a warmed up stake of 2
|
// this one should be able to collect everything from t=0 a warmed up stake of 2
|
||||||
// (2 credits at stake of 1) + (1 credit at a stake of 2)
|
// (2 credits at stake of 1) + (1 credit at a stake of 2)
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some((
|
Some((
|
||||||
0,
|
0,
|
||||||
stake.delegation.stake * 1 + stake.delegation.stake * 2,
|
stake.delegation.stake * 2 // epoch 0
|
||||||
3
|
+ stake.delegation.stake * 1 // epoch 1
|
||||||
|
+ stake.delegation.stake * 1, // epoch 2
|
||||||
|
4
|
||||||
)),
|
)),
|
||||||
stake.calculate_rewards(1.0, &vote_state, None)
|
stake.calculate_rewards(1.0, &vote_state, None)
|
||||||
);
|
);
|
||||||
|
@ -1985,12 +1982,12 @@ mod tests {
|
||||||
// verify that None comes back on small redemptions where no one gets paid
|
// verify that None comes back on small redemptions where no one gets paid
|
||||||
vote_state.commission = 1;
|
vote_state.commission = 1;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
None, // would be Some((0, 2 * 1 + 1 * 2, 3)),
|
None, // would be Some((0, 2 * 1 + 1 * 2, 4)),
|
||||||
stake.calculate_rewards(1.0, &vote_state, None)
|
stake.calculate_rewards(1.0, &vote_state, None)
|
||||||
);
|
);
|
||||||
vote_state.commission = 99;
|
vote_state.commission = 99;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
None, // would be Some((0, 2 * 1 + 1 * 2, 3)),
|
None, // would be Some((0, 2 * 1 + 1 * 2, 4)),
|
||||||
stake.calculate_rewards(1.0, &vote_state, None)
|
stake.calculate_rewards(1.0, &vote_state, None)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -151,14 +151,6 @@ pub struct VoteState {
|
||||||
pub votes: VecDeque<Lockout>,
|
pub votes: VecDeque<Lockout>,
|
||||||
pub root_slot: Option<u64>,
|
pub root_slot: Option<u64>,
|
||||||
|
|
||||||
/// clock epoch
|
|
||||||
epoch: Epoch,
|
|
||||||
/// clock credits earned, monotonically increasing
|
|
||||||
credits: u64,
|
|
||||||
|
|
||||||
/// credits as of previous epoch
|
|
||||||
last_epoch_credits: u64,
|
|
||||||
|
|
||||||
/// history of how many credits earned by the end of each epoch
|
/// history of how many credits earned by the end of each epoch
|
||||||
/// each tuple is (Epoch, credits, prev_credits)
|
/// each tuple is (Epoch, credits, prev_credits)
|
||||||
epoch_credits: Vec<(Epoch, u64, u64)>,
|
epoch_credits: Vec<(Epoch, u64, u64)>,
|
||||||
|
@ -322,31 +314,37 @@ impl VoteState {
|
||||||
|
|
||||||
/// increment credits, record credits for last epoch if new epoch
|
/// increment credits, record credits for last epoch if new epoch
|
||||||
pub fn increment_credits(&mut self, epoch: Epoch) {
|
pub fn increment_credits(&mut self, epoch: Epoch) {
|
||||||
// record credits by epoch
|
// increment credits, record by epoch
|
||||||
|
|
||||||
if epoch != self.epoch {
|
// never seen a credit
|
||||||
// encode the delta, but be able to return partial for stakers who
|
if self.epoch_credits.is_empty() {
|
||||||
// attach halfway through an epoch
|
self.epoch_credits.push((epoch, 0, 0));
|
||||||
if self.credits > 0 {
|
} else if epoch != self.epoch_credits.last().unwrap().0 {
|
||||||
self.epoch_credits
|
let (_, credits, prev_credits) = *self.epoch_credits.last().unwrap();
|
||||||
.push((self.epoch, self.credits, self.last_epoch_credits));
|
|
||||||
|
if credits != prev_credits {
|
||||||
|
// if credits were earned previous epoch
|
||||||
|
// append entry at end of list for the new epoch
|
||||||
|
self.epoch_credits.push((epoch, credits, credits));
|
||||||
|
} else {
|
||||||
|
// else just move the current epoch
|
||||||
|
self.epoch_credits.last_mut().unwrap().0 = epoch;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if stakers do not claim before the epoch goes away they lose the
|
// if stakers do not claim before the epoch goes away they lose the
|
||||||
// credits...
|
// credits...
|
||||||
if self.epoch_credits.len() > MAX_EPOCH_CREDITS_HISTORY {
|
if self.epoch_credits.len() > MAX_EPOCH_CREDITS_HISTORY {
|
||||||
self.epoch_credits.remove(0);
|
self.epoch_credits.remove(0);
|
||||||
}
|
}
|
||||||
self.epoch = epoch;
|
|
||||||
self.last_epoch_credits = self.credits;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.credits += 1;
|
self.epoch_credits.last_mut().unwrap().1 += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// "unchecked" functions used by tests and Tower
|
/// "unchecked" functions used by tests and Tower
|
||||||
pub fn process_vote_unchecked(&mut self, vote: &Vote) {
|
pub fn process_vote_unchecked(&mut self, vote: &Vote) {
|
||||||
let slot_hashes: Vec<_> = vote.slots.iter().rev().map(|x| (*x, vote.hash)).collect();
|
let slot_hashes: Vec<_> = vote.slots.iter().rev().map(|x| (*x, vote.hash)).collect();
|
||||||
let _ignored = self.process_vote(vote, &slot_hashes, self.epoch);
|
let _ignored = self.process_vote(vote, &slot_hashes, self.current_epoch());
|
||||||
}
|
}
|
||||||
pub fn process_slot_vote_unchecked(&mut self, slot: Slot) {
|
pub fn process_slot_vote_unchecked(&mut self, slot: Slot) {
|
||||||
self.process_vote_unchecked(&Vote::new(vec![slot], Hash::default()));
|
self.process_vote_unchecked(&Vote::new(vec![slot], Hash::default()));
|
||||||
|
@ -361,10 +359,22 @@ impl VoteState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn current_epoch(&self) -> Epoch {
|
||||||
|
if self.epoch_credits.is_empty() {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
self.epoch_credits.last().unwrap().0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Number of "credits" owed to this account from the mining pool. Submit this
|
/// Number of "credits" owed to this account from the mining pool. Submit this
|
||||||
/// VoteState to the Rewards program to trade credits for lamports.
|
/// VoteState to the Rewards program to trade credits for lamports.
|
||||||
pub fn credits(&self) -> u64 {
|
pub fn credits(&self) -> u64 {
|
||||||
self.credits
|
if self.epoch_credits.is_empty() {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
self.epoch_credits.last().unwrap().1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Number of "credits" owed to this account from the mining pool on a per-epoch basis,
|
/// Number of "credits" owed to this account from the mining pool on a per-epoch basis,
|
||||||
|
@ -1022,10 +1032,10 @@ mod tests {
|
||||||
vote_state.process_slot_vote_unchecked(i as u64);
|
vote_state.process_slot_vote_unchecked(i as u64);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(vote_state.credits, 0);
|
assert_eq!(vote_state.credits(), 0);
|
||||||
|
|
||||||
vote_state.process_slot_vote_unchecked(MAX_LOCKOUT_HISTORY as u64 + 1);
|
vote_state.process_slot_vote_unchecked(MAX_LOCKOUT_HISTORY as u64 + 1);
|
||||||
assert_eq!(vote_state.credits, 1);
|
assert_eq!(vote_state.credits(), 1);
|
||||||
vote_state.process_slot_vote_unchecked(MAX_LOCKOUT_HISTORY as u64 + 2);
|
vote_state.process_slot_vote_unchecked(MAX_LOCKOUT_HISTORY as u64 + 2);
|
||||||
assert_eq!(vote_state.credits(), 2);
|
assert_eq!(vote_state.credits(), 2);
|
||||||
vote_state.process_slot_vote_unchecked(MAX_LOCKOUT_HISTORY as u64 + 3);
|
vote_state.process_slot_vote_unchecked(MAX_LOCKOUT_HISTORY as u64 + 3);
|
||||||
|
@ -1329,7 +1339,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
expected.push((epoch, credits, credits - epoch));
|
expected.push((epoch, credits, credits - epoch));
|
||||||
}
|
}
|
||||||
expected.pop(); // last one doesn't count, doesn't get saved off
|
|
||||||
while expected.len() > MAX_EPOCH_CREDITS_HISTORY {
|
while expected.len() > MAX_EPOCH_CREDITS_HISTORY {
|
||||||
expected.remove(0);
|
expected.remove(0);
|
||||||
}
|
}
|
||||||
|
@ -1344,10 +1354,10 @@ mod tests {
|
||||||
|
|
||||||
assert_eq!(vote_state.epoch_credits().len(), 0);
|
assert_eq!(vote_state.epoch_credits().len(), 0);
|
||||||
vote_state.increment_credits(1);
|
vote_state.increment_credits(1);
|
||||||
assert_eq!(vote_state.epoch_credits().len(), 0);
|
assert_eq!(vote_state.epoch_credits().len(), 1);
|
||||||
|
|
||||||
vote_state.increment_credits(2);
|
vote_state.increment_credits(2);
|
||||||
assert_eq!(vote_state.epoch_credits().len(), 1);
|
assert_eq!(vote_state.epoch_credits().len(), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
Loading…
Reference in New Issue