Remove RedeemVoteCredits (#7916)

* Move redeem_vote_credits into runtime

* Move redeem_vote_credits into runtime

* Remove RedeemVoteCredits

* chugga for less indentation

* resurrect NoCreditsToRedeem

* fixup
This commit is contained in:
Rob Walker
2020-01-22 16:53:42 -08:00
committed by GitHub
parent 964ff522be
commit 1e2b55c0d7
10 changed files with 38 additions and 532 deletions

View File

@ -1,7 +1,6 @@
use solana_sdk::genesis_config::GenesisConfig;
pub mod config;
pub mod rewards_pools;
pub mod stake_instruction;
pub mod stake_state;
@ -12,9 +11,5 @@ solana_sdk::declare_program!(
);
pub fn add_genesis_accounts(genesis_config: &mut GenesisConfig) -> u64 {
for (pubkey, account) in rewards_pools::create_genesis_accounts() {
genesis_config.add_rewards_pool(pubkey, account);
}
config::add_genesis_account(genesis_config)
}

View File

@ -1,56 +0,0 @@
//! rewards_pools
//! * initialize genesis with rewards pools
//! * keep track of rewards
//! * own mining pools
use crate::stake_state::StakeState;
use rand::{thread_rng, Rng};
use solana_sdk::{
account::Account,
hash::{hash, Hash},
pubkey::Pubkey,
};
// base rewards pool ID
solana_sdk::declare_id!("StakeRewards1111111111111111111111111111111");
// to cut down on collisions for redemptions, we make multiple accounts
pub const NUM_REWARDS_POOLS: usize = 256;
pub fn random_id() -> Pubkey {
let mut id = Hash::new(id().as_ref());
for _i in 0..thread_rng().gen_range(0, NUM_REWARDS_POOLS) {
id = hash(id.as_ref());
}
Pubkey::new(id.as_ref())
}
pub fn create_genesis_accounts() -> Vec<(Pubkey, Account)> {
let mut accounts = Vec::with_capacity(NUM_REWARDS_POOLS);
let mut pubkey = id();
for _i in 0..NUM_REWARDS_POOLS {
accounts.push((
pubkey,
Account::new_data(std::u64::MAX, &StakeState::RewardsPool, &crate::id()).unwrap(),
));
pubkey = Pubkey::new(hash(pubkey.as_ref()).as_ref());
}
accounts
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test() {
let accounts = create_genesis_accounts();
for _i in 0..NUM_REWARDS_POOLS {
let id = random_id();
assert!(accounts.iter().position(|x| x.0 == id).is_some());
}
}
}

View File

@ -11,9 +11,7 @@ use solana_sdk::{
instruction_processor_utils::{limited_deserialize, next_keyed_account, DecodeError},
pubkey::Pubkey,
system_instruction,
sysvar::{
self, clock::Clock, rent::Rent, rewards::Rewards, stake_history::StakeHistory, Sysvar,
},
sysvar::{self, clock::Clock, rent::Rent, stake_history::StakeHistory, Sysvar},
};
use thiserror::Error;
@ -80,17 +78,6 @@ pub enum StakeInstruction {
///
DelegateStake,
/// Redeem credits in the stake account
///
/// Expects 5 Accounts:
/// 0 - StakeAccount to be updated with rewards
/// 1 - VoteAccount to which the Stake is delegated,
/// 2 - RewardsPool Stake Account from which to redeem credits
/// 3 - Rewards sysvar Account that carries points values
/// 4 - StakeHistory sysvar that carries stake warmup/cooldown history
///
RedeemVoteCredits,
/// Split u64 tokens and stake off a stake account into another stake
/// account. Requires Authorized::staker signature and the
/// signature of the split-off stake address.
@ -302,17 +289,6 @@ pub fn authorize(
)
}
pub fn redeem_vote_credits(stake_pubkey: &Pubkey, vote_pubkey: &Pubkey) -> Instruction {
let account_metas = vec![
AccountMeta::new(*stake_pubkey, false),
AccountMeta::new(*vote_pubkey, false),
AccountMeta::new(crate::rewards_pools::random_id(), false),
AccountMeta::new_readonly(sysvar::rewards::id(), false),
AccountMeta::new_readonly(sysvar::stake_history::id(), false),
];
Instruction::new(id(), &StakeInstruction::RedeemVoteCredits, account_metas)
}
pub fn delegate_stake(
stake_pubkey: &Pubkey,
authorized_pubkey: &Pubkey,
@ -410,17 +386,6 @@ pub fn process_instruction(
&signers,
)
}
StakeInstruction::RedeemVoteCredits => {
let vote = &mut next_keyed_account(keyed_accounts)?;
let rewards_pool = &mut next_keyed_account(keyed_accounts)?;
me.redeem_vote_credits(
vote,
rewards_pool,
&Rewards::from_keyed_account(next_keyed_account(keyed_accounts)?)?,
&StakeHistory::from_keyed_account(next_keyed_account(keyed_accounts)?)?,
)
}
StakeInstruction::Split(lamports) => {
let split_stake = &mut next_keyed_account(keyed_accounts)?;
me.split(lamports, split_stake, &signers)
@ -496,10 +461,6 @@ mod tests {
)),
Err(InstructionError::InvalidAccountData),
);
assert_eq!(
process_instruction(&redeem_vote_credits(&Pubkey::default(), &Pubkey::default())),
Err(InstructionError::InvalidAccountData),
);
assert_eq!(
process_instruction(&authorize(
&Pubkey::default(),
@ -658,35 +619,6 @@ mod tests {
Err(InstructionError::NotEnoughAccountKeys),
);
// catches the number of args check
assert_eq!(
super::process_instruction(
&Pubkey::default(),
&mut [
KeyedAccount::new(&Pubkey::default(), false, &mut create_default_account()),
KeyedAccount::new(&Pubkey::default(), false, &mut create_default_account()),
],
&serialize(&StakeInstruction::RedeemVoteCredits).unwrap(),
),
Err(InstructionError::NotEnoughAccountKeys),
);
// catches the type of args check
assert_eq!(
super::process_instruction(
&Pubkey::default(),
&mut [
KeyedAccount::new(&Pubkey::default(), false, &mut create_default_account()),
KeyedAccount::new(&Pubkey::default(), false, &mut create_default_account()),
KeyedAccount::new(&Pubkey::default(), false, &mut create_default_account()),
KeyedAccount::new(&Pubkey::default(), false, &mut create_default_account()),
KeyedAccount::new(&Pubkey::default(), false, &mut create_default_account()),
],
&serialize(&StakeInstruction::RedeemVoteCredits).unwrap(),
),
Err(InstructionError::InvalidArgument),
);
// gets the check non-deserialize-able account in delegate_stake
assert_eq!(
super::process_instruction(
@ -710,33 +642,6 @@ mod tests {
Err(InstructionError::InvalidAccountData),
);
// gets the deserialization checks in redeem_vote_credits
assert_eq!(
super::process_instruction(
&Pubkey::default(),
&mut [
KeyedAccount::new(&Pubkey::default(), false, &mut create_default_account()),
KeyedAccount::new(&Pubkey::default(), false, &mut create_default_account()),
KeyedAccount::new(&Pubkey::default(), false, &mut create_default_account()),
KeyedAccount::new(
&sysvar::rewards::id(),
false,
&mut RefCell::new(sysvar::rewards::create_account(1, 0.0, 0.0))
),
KeyedAccount::new(
&sysvar::stake_history::id(),
false,
&mut RefCell::new(sysvar::stake_history::create_account(
1,
&StakeHistory::default()
))
),
],
&serialize(&StakeInstruction::RedeemVoteCredits).unwrap(),
),
Err(InstructionError::InvalidAccountData),
);
// Tests 3rd keyed account is of correct type (Clock instead of rewards) in withdraw
assert_eq!(
super::process_instruction(

View File

@ -408,8 +408,8 @@ impl Stake {
// the staker registered sometime during the epoch, partial credit
credits - credits_observed
} else {
// the staker has already observed/redeemed this epoch, or activated
// after this epoch
// the staker has already observed or been redeemed this epoch
// or was activated after this epoch
0
};
@ -537,13 +537,6 @@ pub trait StakeAccount {
clock: &sysvar::clock::Clock,
signers: &HashSet<Pubkey>,
) -> Result<(), InstructionError>;
fn redeem_vote_credits(
&mut self,
vote_account: &mut KeyedAccount,
rewards_account: &mut KeyedAccount,
rewards: &sysvar::rewards::Rewards,
stake_history: &sysvar::stake_history::StakeHistory,
) -> Result<(), InstructionError>;
fn split(
&mut self,
lamports: u64,
@ -647,51 +640,6 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
Err(InstructionError::InvalidAccountData)
}
}
fn redeem_vote_credits(
&mut self,
vote_account: &mut KeyedAccount,
rewards_account: &mut KeyedAccount,
rewards: &sysvar::rewards::Rewards,
stake_history: &sysvar::stake_history::StakeHistory,
) -> Result<(), InstructionError> {
if let (StakeState::Stake(meta, mut stake), StakeState::RewardsPool) =
(self.state()?, rewards_account.state()?)
{
let vote_state: VoteState = vote_account.state()?;
// the only valid use of current voter_pubkey, redelegation breaks
// rewards redemption for previous voter_pubkey
if stake.delegation.voter_pubkey != *vote_account.unsigned_key() {
return Err(InstructionError::InvalidArgument);
}
if let Some((voters_reward, stakers_reward, credits_observed)) = stake
.calculate_rewards(
rewards.validator_point_value,
&vote_state,
Some(stake_history),
)
{
if rewards_account.lamports()? < (stakers_reward + voters_reward) {
return Err(InstructionError::UnbalancedInstruction);
}
rewards_account.try_account_ref_mut()?.lamports -= stakers_reward + voters_reward;
self.try_account_ref_mut()?.lamports += stakers_reward;
vote_account.try_account_ref_mut()?.lamports += voters_reward;
stake.credits_observed = credits_observed;
stake.delegation.stake += stakers_reward;
self.set_state(&StakeState::Stake(meta, stake))
} else {
// not worth collecting
Err(StakeError::NoCreditsToRedeem.into())
}
} else {
Err(InstructionError::InvalidAccountData)
}
}
fn split(
&mut self,
@ -2085,166 +2033,6 @@ mod tests {
);
}
#[test]
fn test_stake_redeem_vote_credits() {
let clock = sysvar::clock::Clock::default();
let mut rewards = sysvar::rewards::Rewards::default();
rewards.validator_point_value = 100.0;
let rewards_pool_pubkey = Pubkey::new_rand();
let mut rewards_pool_account = Account::new_ref_data(
std::u64::MAX,
&StakeState::RewardsPool,
&crate::rewards_pools::id(),
)
.unwrap();
let mut rewards_pool_keyed_account =
KeyedAccount::new(&rewards_pool_pubkey, false, &mut rewards_pool_account);
let stake_pubkey = Pubkey::default();
let stake_lamports = 100;
let mut stake_account = Account::new_ref_data_with_space(
stake_lamports,
&StakeState::Initialized(Meta::auto(&stake_pubkey)),
std::mem::size_of::<StakeState>(),
&id(),
)
.expect("stake_account");
let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &mut stake_account);
let vote_pubkey = Pubkey::new_rand();
let mut vote_account = RefCell::new(vote_state::create_account(
&vote_pubkey,
&Pubkey::new_rand(),
0,
100,
));
let mut vote_keyed_account = KeyedAccount::new(&vote_pubkey, false, &mut vote_account);
// not delegated yet, deserialization fails
assert_eq!(
stake_keyed_account.redeem_vote_credits(
&mut vote_keyed_account,
&mut rewards_pool_keyed_account,
&rewards,
&StakeHistory::default(),
),
Err(InstructionError::InvalidAccountData)
);
let signers = vec![stake_pubkey].into_iter().collect();
// delegate the stake
assert!(stake_keyed_account
.delegate_stake(&vote_keyed_account, &clock, &Config::default(), &signers)
.is_ok());
let stake_history = create_stake_history_from_delegations(
Some(100),
0..10,
&[
StakeState::stake_from(&stake_keyed_account.account.borrow())
.unwrap()
.delegation,
],
);
// no credits to claim
assert_eq!(
stake_keyed_account.redeem_vote_credits(
&mut vote_keyed_account,
&mut rewards_pool_keyed_account,
&rewards,
&stake_history,
),
Err(StakeError::NoCreditsToRedeem.into())
);
// in this call, we've swapped rewards and vote, deserialization of rewards_pool fails
assert_eq!(
stake_keyed_account.redeem_vote_credits(
&mut rewards_pool_keyed_account,
&mut vote_keyed_account,
&rewards,
&StakeHistory::default(),
),
Err(InstructionError::InvalidAccountData)
);
let mut vote_account = RefCell::new(vote_state::create_account(
&vote_pubkey,
&Pubkey::new_rand(),
0,
100,
));
let mut vote_state = VoteState::from(&vote_account.borrow()).unwrap();
// split credits 3:1 between staker and voter
vote_state.commission = 25;
// put in some credits in epoch 0 for which we should have a non-zero stake
for _i in 0..100 {
vote_state.increment_credits(1);
}
vote_state.increment_credits(2);
vote_state.to(&mut vote_account.borrow_mut()).unwrap();
let mut vote_keyed_account = KeyedAccount::new(&vote_pubkey, false, &mut vote_account);
// some credits to claim, but rewards pool empty (shouldn't ever happen)
rewards_pool_keyed_account.account.borrow_mut().lamports = 1;
assert_eq!(
stake_keyed_account.redeem_vote_credits(
&mut vote_keyed_account,
&mut rewards_pool_keyed_account,
&rewards,
&StakeHistory::default(),
),
Err(InstructionError::UnbalancedInstruction)
);
rewards_pool_keyed_account.account.borrow_mut().lamports = std::u64::MAX;
// finally! some credits to claim
let stake_account_balance = stake_keyed_account.account.borrow().lamports;
let vote_account_balance = vote_keyed_account.account.borrow().lamports;
assert_eq!(
stake_keyed_account.redeem_vote_credits(
&mut vote_keyed_account,
&mut rewards_pool_keyed_account,
&rewards,
&stake_history,
),
Ok(())
);
let staker_rewards = stake_keyed_account.account.borrow().lamports - stake_account_balance;
let voter_commission = vote_keyed_account.account.borrow().lamports - vote_account_balance;
assert!(voter_commission > 0);
assert!(staker_rewards > 0);
assert!(
staker_rewards / 3 >= voter_commission,
"rewards should be split ~3:1"
);
// verify rewards are added to stake
let stake = StakeState::stake_from(&stake_keyed_account.account.borrow()).unwrap();
assert_eq!(
stake.delegation.stake,
stake_keyed_account.account.borrow().lamports
);
let wrong_vote_pubkey = Pubkey::new_rand();
let mut wrong_vote_keyed_account =
KeyedAccount::new(&wrong_vote_pubkey, false, &mut vote_account);
// wrong voter_pubkey...
assert_eq!(
stake_keyed_account.redeem_vote_credits(
&mut wrong_vote_keyed_account,
&mut rewards_pool_keyed_account,
&rewards,
&stake_history,
),
Err(InstructionError::InvalidArgument)
);
}
#[test]
fn test_authorize_uninit() {
let stake_pubkey = Pubkey::new_rand();