diff --git a/core/src/consensus.rs b/core/src/consensus.rs index fef6af8393..283ed6b1fe 100644 --- a/core/src/consensus.rs +++ b/core/src/consensus.rs @@ -214,8 +214,7 @@ impl Tower { } pub fn is_recent_epoch(&self, bank: &Bank) -> bool { - let bank_epoch = bank.get_epoch_and_slot_index(bank.slot()).0; - bank_epoch >= self.epoch_stakes.epoch + bank.epoch() >= self.epoch_stakes.epoch } pub fn update_epoch(&mut self, bank: &Bank) { @@ -224,8 +223,7 @@ impl Tower { bank.slot(), self.epoch_stakes.epoch ); - let bank_epoch = bank.get_epoch_and_slot_index(bank.slot()).0; - if bank_epoch != self.epoch_stakes.epoch { + if bank.epoch() != self.epoch_stakes.epoch { assert!( self.is_recent_epoch(bank), "epoch_stakes cannot move backwards" diff --git a/core/src/staking_utils.rs b/core/src/staking_utils.rs index 94d75c9307..55791c41ab 100644 --- a/core/src/staking_utils.rs +++ b/core/src/staking_utils.rs @@ -150,23 +150,15 @@ pub(crate) mod tests { // First epoch has the bootstrap leader expected.insert(voting_keypair.pubkey(), leader_stake.stake(0)); - assert_eq!( - vote_account_stakes_at_epoch(&bank, 0), - Some(expected.clone()) - ); + + // henceforth, verify that we have snapshots of stake at epoch 0 + let expected = Some(expected); + assert_eq!(vote_account_stakes_at_epoch(&bank, 0), expected); // Second epoch carries same information let bank = new_from_parent(&Arc::new(bank), 1); - assert_eq!( - vote_account_stakes_at_epoch(&bank, 0), - Some(expected.clone()) - ); - - expected.insert(voting_keypair.pubkey(), leader_stake.stake(1)); - assert_eq!( - vote_account_stakes_at_epoch(&bank, 1), - Some(expected.clone()) - ); + assert_eq!(vote_account_stakes_at_epoch(&bank, 0), expected); + assert_eq!(vote_account_stakes_at_epoch(&bank, 1), expected); } pub(crate) fn setup_vote_and_stake_accounts( @@ -233,7 +225,7 @@ pub(crate) mod tests { .. } = create_genesis_block(10_000); - let mut bank = Bank::new(&genesis_block); + let bank = Bank::new(&genesis_block); let vote_pubkey = Pubkey::new_rand(); // Give the validator some stake but don't setup a staking account @@ -252,23 +244,28 @@ pub(crate) mod tests { stake, ); + // simulated stake let other_stake = Stake { stake, - activated: bank.get_stakers_epoch(bank.slot()), + activated: bank.epoch(), ..Stake::default() }; let epoch = bank.get_stakers_epoch(bank.slot()); // find the first slot in the next staker's epoch - while bank.epoch() <= epoch { - let slot = bank.slot() + 1; - bank = new_from_parent(&Arc::new(bank), slot); + let mut slot = 1; + while bank.get_stakers_epoch(slot) <= epoch { + slot += 1; } + let bank = new_from_parent(&Arc::new(bank), slot); + let epoch = bank.get_stakers_epoch(slot); let result: Vec<_> = epoch_stakes_and_lockouts(&bank, 0); assert_eq!(result, vec![(leader_stake.stake(0), None)]); - let mut result: Vec<_> = epoch_stakes_and_lockouts(&bank, bank.epoch()); + // epoch stakes and lockouts are saved off for the future epoch, should + // match current bank state + let mut result: Vec<_> = epoch_stakes_and_lockouts(&bank, epoch); result.sort(); let mut expected = vec![ (leader_stake.stake(bank.epoch()), None), diff --git a/programs/stake_api/src/stake_state.rs b/programs/stake_api/src/stake_state.rs index 1474ded5b0..fddab9a70e 100644 --- a/programs/stake_api/src/stake_state.rs +++ b/programs/stake_api/src/stake_state.rs @@ -217,7 +217,7 @@ impl<'a> StakeAccount for KeyedAccount<'a> { new_stake, vote_account.unsigned_key(), &vote_account.state()?, - clock.stakers_epoch, + clock.epoch, ); self.set_state(&StakeState::Stake(stake)) @@ -231,7 +231,7 @@ impl<'a> StakeAccount for KeyedAccount<'a> { } if let StakeState::Stake(mut stake) = self.state()? { - stake.deactivate(clock.stakers_epoch); + stake.deactivate(clock.epoch); self.set_state(&StakeState::Stake(stake)) } else { @@ -291,7 +291,7 @@ impl<'a> StakeAccount for KeyedAccount<'a> { if stake.deactivated == std::u64::MAX { return Err(InstructionError::InsufficientFunds); } - let staked = if stake.stake(clock.stakers_epoch) == 0 { + let staked = if stake.stake(clock.epoch) == 0 { 0 } else { // Assume full stake if the stake is under warmup/cooldown @@ -358,7 +358,7 @@ mod tests { #[test] fn test_stake_delegate_stake() { let clock = sysvar::clock::Clock { - stakers_epoch: 1, + epoch: 1, ..sysvar::clock::Clock::default() }; @@ -406,7 +406,7 @@ mod tests { voter_pubkey: vote_keypair.pubkey(), credits_observed: vote_state.credits(), stake: stake_lamports, - activated: clock.stakers_epoch, + activated: clock.epoch, deactivated: std::u64::MAX, }) ); @@ -464,7 +464,7 @@ mod tests { Account::new(stake_lamports, std::mem::size_of::(), &id()); let clock = sysvar::clock::Clock { - stakers_epoch: 1, + epoch: 1, ..sysvar::clock::Clock::default() }; @@ -548,7 +548,7 @@ mod tests { // deactivate the stake before withdrawal assert_eq!(stake_keyed_account.deactivate_stake(&clock), Ok(())); // simulate time passing - clock.stakers_epoch += STAKE_WARMUP_EPOCHS; + clock.epoch += STAKE_WARMUP_EPOCHS; // Try to withdraw more than what's available assert_eq!( @@ -581,7 +581,7 @@ mod tests { let clock = sysvar::clock::Clock::default(); let mut future = sysvar::clock::Clock::default(); - future.stakers_epoch += 16; + future.epoch += 16; let to = Pubkey::new_rand(); let mut to_account = Account::new(1, 0, &system_program::id()); diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index e65d8fe56c..27a80bd2a0 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -301,8 +301,7 @@ impl Bank { { let stakes = bank.stakes.read().unwrap(); for epoch in 0..=bank.get_stakers_epoch(bank.slot) { - bank.epoch_stakes - .insert(epoch, stakes.clone_with_epoch(epoch)); + bank.epoch_stakes.insert(epoch, stakes.clone()); } } bank.update_clock(); @@ -372,7 +371,7 @@ impl Bank { // if my parent didn't populate for this epoch, we've // crossed a boundary if epoch_stakes.get(&epoch).is_none() { - epoch_stakes.insert(epoch, self.stakes.read().unwrap().clone_with_epoch(epoch)); + epoch_stakes.insert(epoch, self.stakes.read().unwrap().clone()); } epoch_stakes }; @@ -2488,8 +2487,10 @@ mod tests { let vote_accounts = parent.epoch_vote_accounts(epoch); assert!(vote_accounts.is_some()); + // epoch_stakes are a snapshot at the stakers_slot_offset boundary + // in the prior epoch (0 in this case) assert_eq!( - leader_stake.stake(epoch), + leader_stake.stake(0), vote_accounts.unwrap().get(&leader_vote_account).unwrap().0 ); @@ -2505,7 +2506,7 @@ mod tests { assert!(child.epoch_vote_accounts(epoch).is_some()); assert_eq!( - leader_stake.stake(epoch), + leader_stake.stake(child.epoch()), child .epoch_vote_accounts(epoch) .unwrap() @@ -2515,13 +2516,22 @@ mod tests { ); // child crosses epoch boundary but isn't the first slot in the epoch, still - // makes an epoch stakes + // makes an epoch stakes snapshot at 1 let child = Bank::new_from_parent( &parent, &leader_pubkey, SLOTS_PER_EPOCH - (STAKERS_SLOT_OFFSET % SLOTS_PER_EPOCH) + 1, ); assert!(child.epoch_vote_accounts(epoch).is_some()); + assert_eq!( + leader_stake.stake(child.epoch()), + child + .epoch_vote_accounts(epoch) + .unwrap() + .get(&leader_vote_account) + .unwrap() + .0 + ); } #[test]