add stake warmup and cool down (#4711)

This commit is contained in:
Rob Walker 2019-06-17 19:34:21 -07:00 committed by GitHub
parent 9cafd1f85e
commit 0ff9c4cd8e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 236 additions and 128 deletions

View File

@ -6,16 +6,11 @@ use serde_derive::{Deserialize, Serialize};
use solana_sdk::account::KeyedAccount; use solana_sdk::account::KeyedAccount;
use solana_sdk::instruction::{AccountMeta, Instruction, InstructionError}; use solana_sdk::instruction::{AccountMeta, Instruction, InstructionError};
use solana_sdk::pubkey::Pubkey; use solana_sdk::pubkey::Pubkey;
use solana_sdk::syscall;
use solana_sdk::system_instruction; use solana_sdk::system_instruction;
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum StakeInstruction { pub enum StakeInstruction {
/// Initialize the stake account as a Stake account.
///
/// Expects 1 Accounts:
/// 0 - StakeAccount to be initialized
InitializeStake,
// Initialize the stake account as a MiningPool account // Initialize the stake account as a MiningPool account
/// ///
/// Expects 1 Accounts: /// Expects 1 Accounts:
@ -25,7 +20,7 @@ pub enum StakeInstruction {
/// `Delegate` a stake to a particular node /// `Delegate` a stake to a particular node
/// ///
/// Expects 2 Accounts: /// Expects 2 Accounts:
/// 0 - Delegate StakeAccount to be updated <= must have this signature /// 0 - Uninitialized StakeAccount to be delegated <= must have this signature
/// 1 - VoteAccount to which this Stake will be delegated /// 1 - VoteAccount to which this Stake will be delegated
/// ///
/// The u64 is the portion of the Stake account balance to be activated, /// The u64 is the portion of the Stake account balance to be activated,
@ -48,20 +43,13 @@ pub fn create_stake_account(
staker_pubkey: &Pubkey, staker_pubkey: &Pubkey,
lamports: u64, lamports: u64,
) -> Vec<Instruction> { ) -> Vec<Instruction> {
vec![ vec![system_instruction::create_account(
system_instruction::create_account( from_pubkey,
from_pubkey, staker_pubkey,
staker_pubkey, lamports,
lamports, std::mem::size_of::<StakeState>() as u64,
std::mem::size_of::<StakeState>() as u64, &id(),
&id(), )]
),
Instruction::new(
id(),
&StakeInstruction::InitializeStake,
vec![AccountMeta::new(*staker_pubkey, false)],
),
]
} }
pub fn create_stake_account_and_delegate_stake( pub fn create_stake_account_and_delegate_stake(
@ -113,10 +101,16 @@ pub fn delegate_stake(stake_pubkey: &Pubkey, vote_pubkey: &Pubkey, stake: u64) -
let account_metas = vec![ let account_metas = vec![
AccountMeta::new(*stake_pubkey, true), AccountMeta::new(*stake_pubkey, true),
AccountMeta::new(*vote_pubkey, false), AccountMeta::new(*vote_pubkey, false),
AccountMeta::new(syscall::current::id(), false),
]; ];
Instruction::new(id(), &StakeInstruction::DelegateStake(stake), account_metas) Instruction::new(id(), &StakeInstruction::DelegateStake(stake), account_metas)
} }
fn current(current_account: &KeyedAccount) -> Result<syscall::current::Current, InstructionError> {
syscall::current::Current::from(current_account.account)
.ok_or(InstructionError::InvalidArgument)
}
pub fn process_instruction( pub fn process_instruction(
_program_id: &Pubkey, _program_id: &Pubkey,
keyed_accounts: &mut [KeyedAccount], keyed_accounts: &mut [KeyedAccount],
@ -142,18 +136,13 @@ pub fn process_instruction(
} }
me.initialize_mining_pool() me.initialize_mining_pool()
} }
StakeInstruction::InitializeStake => {
if !rest.is_empty() {
Err(InstructionError::InvalidInstructionData)?;
}
me.initialize_stake()
}
StakeInstruction::DelegateStake(stake) => { StakeInstruction::DelegateStake(stake) => {
if rest.len() != 1 { if rest.len() != 2 {
Err(InstructionError::InvalidInstructionData)?; Err(InstructionError::InvalidInstructionData)?;
} }
let vote = &rest[0]; let vote = &rest[0];
me.delegate_stake(vote, stake)
me.delegate_stake(vote, stake, &current(&rest[1])?)
} }
StakeInstruction::RedeemVoteCredits => { StakeInstruction::RedeemVoteCredits => {
if rest.len() != 2 { if rest.len() != 2 {
@ -175,10 +164,18 @@ mod tests {
use solana_sdk::account::Account; use solana_sdk::account::Account;
fn process_instruction(instruction: &Instruction) -> Result<(), InstructionError> { fn process_instruction(instruction: &Instruction) -> Result<(), InstructionError> {
let mut accounts = vec![]; let mut accounts: Vec<_> = instruction
for _ in 0..instruction.accounts.len() { .accounts
accounts.push(Account::default()); .iter()
} .map(|meta| {
if syscall::current::check_id(&meta.pubkey) {
syscall::current::create_account(1, 0, 0, 0)
} else {
Account::default()
}
})
.collect();
{ {
let mut keyed_accounts: Vec<_> = instruction let mut keyed_accounts: Vec<_> = instruction
.accounts .accounts
@ -250,13 +247,18 @@ mod tests {
Err(InstructionError::InvalidInstructionData), Err(InstructionError::InvalidInstructionData),
); );
// gets the check in delegate_stake // gets the check non-deserialize-able account in delegate_stake
assert_eq!( assert_eq!(
super::process_instruction( super::process_instruction(
&Pubkey::default(), &Pubkey::default(),
&mut [ &mut [
KeyedAccount::new(&Pubkey::default(), true, &mut Account::default()), KeyedAccount::new(&Pubkey::default(), true, &mut Account::default()),
KeyedAccount::new(&Pubkey::default(), false, &mut Account::default()), KeyedAccount::new(&Pubkey::default(), false, &mut Account::default()),
KeyedAccount::new(
&syscall::current::id(),
false,
&mut syscall::current::create_account(1, 0, 0, 0)
),
], ],
&serialize(&StakeInstruction::DelegateStake(0)).unwrap(), &serialize(&StakeInstruction::DelegateStake(0)).unwrap(),
), ),

View File

@ -9,16 +9,13 @@ use solana_sdk::account::{Account, KeyedAccount};
use solana_sdk::account_utils::State; use solana_sdk::account_utils::State;
use solana_sdk::instruction::InstructionError; use solana_sdk::instruction::InstructionError;
use solana_sdk::pubkey::Pubkey; use solana_sdk::pubkey::Pubkey;
use solana_sdk::syscall;
use solana_vote_api::vote_state::VoteState; use solana_vote_api::vote_state::VoteState;
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] #[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
pub enum StakeState { pub enum StakeState {
Uninitialized, Uninitialized,
Stake { Stake(Stake),
voter_pubkey: Pubkey,
credits_observed: u64,
stake: u64,
},
MiningPool { MiningPool {
/// epoch for which this Pool will redeem rewards /// epoch for which this Pool will redeem rewards
epoch: u64, epoch: u64,
@ -62,19 +59,26 @@ impl StakeState {
Self::from(account).and_then(|state: Self| state.voter_pubkey_and_stake()) Self::from(account).and_then(|state: Self| state.voter_pubkey_and_stake())
} }
pub fn stake_from(account: &Account) -> Option<(Stake)> {
Self::from(account).and_then(|state: Self| state.stake())
}
pub fn voter_pubkey(&self) -> Option<Pubkey> { pub fn voter_pubkey(&self) -> Option<Pubkey> {
match self { match self {
StakeState::Stake { voter_pubkey, .. } => Some(*voter_pubkey), StakeState::Stake(stake) => Some(stake.voter_pubkey),
_ => None, _ => None,
} }
} }
pub fn stake(&self) -> Option<Stake> {
match self {
StakeState::Stake(stake) => Some(stake.clone()),
_ => None,
}
}
pub fn voter_pubkey_and_stake(&self) -> Option<(Pubkey, u64)> { pub fn voter_pubkey_and_stake(&self) -> Option<(Pubkey, u64)> {
match self { match self {
StakeState::Stake { StakeState::Stake(stake) => Some((stake.voter_pubkey, stake.stake)),
voter_pubkey,
stake,
..
} => Some((*voter_pubkey, *stake)),
_ => None, _ => None,
} }
} }
@ -109,13 +113,76 @@ impl StakeState {
} }
} }
#[derive(Default, Debug, Serialize, Deserialize, PartialEq, Clone)]
pub struct Stake {
pub voter_pubkey: Pubkey,
pub credits_observed: u64,
pub stake: u64, // activated stake
pub epoch: u64, // epoch the stake was activated
pub prev_stake: u64, // for warmup, cooldown
}
const STAKE_WARMUP_COOLDOWN_EPOCHS: u64 = 3;
impl Stake {
pub fn stake(&mut self, epoch: u64) -> u64 {
// prev_stake for stuff in the past
if epoch < self.epoch {
return self.prev_stake;
}
if epoch - self.epoch >= STAKE_WARMUP_COOLDOWN_EPOCHS {
return self.stake;
}
if self.stake != 0 {
// warmup
// 1/3rd, then 2/3rds...
(self.stake / STAKE_WARMUP_COOLDOWN_EPOCHS) * (epoch - self.epoch + 1)
} else if self.prev_stake != 0 {
// cool down
// 3/3rds, then 2/3rds...
self.prev_stake
- ((self.prev_stake / STAKE_WARMUP_COOLDOWN_EPOCHS) * (epoch - self.epoch))
} else {
0
}
}
fn delegate(
&mut self,
stake: u64,
voter_pubkey: &Pubkey,
vote_state: &VoteState,
epoch: u64, // current: &syscall::current::Current
) {
// resets the current stake's credits
self.voter_pubkey = *voter_pubkey;
self.credits_observed = vote_state.credits();
// when this stake was activated
self.epoch = epoch;
self.stake = stake;
}
fn deactivate(&mut self, epoch: u64) {
self.voter_pubkey = Pubkey::default();
self.credits_observed = std::u64::MAX;
self.prev_stake = self.stake(epoch);
self.stake = 0;
self.epoch = epoch;
}
}
pub trait StakeAccount { pub trait StakeAccount {
fn initialize_mining_pool(&mut self) -> Result<(), InstructionError>; fn initialize_mining_pool(&mut self) -> Result<(), InstructionError>;
fn initialize_stake(&mut self) -> Result<(), InstructionError>;
fn delegate_stake( fn delegate_stake(
&mut self, &mut self,
vote_account: &KeyedAccount, vote_account: &KeyedAccount,
stake: u64, stake: u64,
current: &syscall::current::Current,
) -> Result<(), InstructionError>;
fn deactivate_stake(
&mut self,
current: &syscall::current::Current,
) -> Result<(), InstructionError>; ) -> Result<(), InstructionError>;
fn redeem_vote_credits( fn redeem_vote_credits(
&mut self, &mut self,
@ -135,36 +202,49 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
Err(InstructionError::InvalidAccountData) Err(InstructionError::InvalidAccountData)
} }
} }
fn initialize_stake(&mut self) -> Result<(), InstructionError> {
if let StakeState::Uninitialized = self.state()? { fn deactivate_stake(
self.set_state(&StakeState::Stake {
voter_pubkey: Pubkey::default(),
credits_observed: 0,
stake: 0,
})
} else {
Err(InstructionError::InvalidAccountData)
}
}
fn delegate_stake(
&mut self, &mut self,
vote_account: &KeyedAccount, current: &syscall::current::Current,
stake: u64,
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
if self.signer_key().is_none() { if self.signer_key().is_none() {
return Err(InstructionError::MissingRequiredSignature); return Err(InstructionError::MissingRequiredSignature);
} }
if stake > self.account.lamports {
if let StakeState::Stake(mut stake) = self.state()? {
stake.deactivate(current.epoch);
self.set_state(&StakeState::Stake(stake))
} else {
Err(InstructionError::InvalidAccountData)
}
}
fn delegate_stake(
&mut self,
vote_account: &KeyedAccount,
new_stake: u64,
current: &syscall::current::Current,
) -> Result<(), InstructionError> {
if self.signer_key().is_none() {
return Err(InstructionError::MissingRequiredSignature);
}
if new_stake > self.account.lamports {
return Err(InstructionError::InsufficientFunds); return Err(InstructionError::InsufficientFunds);
} }
if let StakeState::Stake { .. } = self.state()? { if let StakeState::Uninitialized = self.state()? {
let vote_state: VoteState = vote_account.state()?; let mut stake = Stake::default();
self.set_state(&StakeState::Stake {
voter_pubkey: *vote_account.unsigned_key(), stake.delegate(
credits_observed: vote_state.credits(), new_stake,
stake, vote_account.unsigned_key(),
}) &vote_account.state()?,
current.epoch,
);
self.set_state(&StakeState::Stake(stake))
} else { } else {
Err(InstructionError::InvalidAccountData) Err(InstructionError::InvalidAccountData)
} }
@ -175,27 +255,21 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
stake_account: &mut KeyedAccount, stake_account: &mut KeyedAccount,
vote_account: &mut KeyedAccount, vote_account: &mut KeyedAccount,
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
if let ( if let (StakeState::MiningPool { .. }, StakeState::Stake(mut stake)) =
StakeState::MiningPool { .. }, (self.state()?, stake_account.state()?)
StakeState::Stake {
voter_pubkey,
credits_observed,
..
},
) = (self.state()?, stake_account.state()?)
{ {
let vote_state: VoteState = vote_account.state()?; let vote_state: VoteState = vote_account.state()?;
if voter_pubkey != *vote_account.unsigned_key() { if stake.voter_pubkey != *vote_account.unsigned_key() {
return Err(InstructionError::InvalidArgument); return Err(InstructionError::InvalidArgument);
} }
if credits_observed > vote_state.credits() { if stake.credits_observed > vote_state.credits() {
return Err(InstructionError::InvalidAccountData); return Err(InstructionError::InvalidAccountData);
} }
if let Some((stakers_reward, voters_reward)) = StakeState::calculate_rewards( if let Some((stakers_reward, voters_reward)) = StakeState::calculate_rewards(
credits_observed, stake.credits_observed,
stake_account.account.lamports, stake_account.account.lamports,
&vote_state, &vote_state,
) { ) {
@ -206,11 +280,9 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
stake_account.account.lamports += stakers_reward; stake_account.account.lamports += stakers_reward;
vote_account.account.lamports += voters_reward; vote_account.account.lamports += voters_reward;
stake_account.set_state(&StakeState::Stake { stake.credits_observed = vote_state.credits();
voter_pubkey,
credits_observed: vote_state.credits(), stake_account.set_state(&StakeState::Stake(stake))
stake: 0,
})
} else { } else {
// not worth collecting // not worth collecting
Err(InstructionError::CustomError(1)) Err(InstructionError::CustomError(1))
@ -230,11 +302,13 @@ pub fn create_stake_account(
let mut stake_account = Account::new(lamports, std::mem::size_of::<StakeState>(), &id()); let mut stake_account = Account::new(lamports, std::mem::size_of::<StakeState>(), &id());
stake_account stake_account
.set_state(&StakeState::Stake { .set_state(&StakeState::Stake(Stake {
voter_pubkey: *voter_pubkey, voter_pubkey: *voter_pubkey,
credits_observed: vote_state.credits(), credits_observed: vote_state.credits(),
stake: lamports, stake: lamports,
}) epoch: 0,
prev_stake: lamports,
}))
.expect("set_state"); .expect("set_state");
stake_account stake_account
@ -262,6 +336,8 @@ mod tests {
#[test] #[test]
fn test_stake_delegate_stake() { fn test_stake_delegate_stake() {
let current = syscall::current::Current::default();
let vote_keypair = Keypair::new(); let vote_keypair = Keypair::new();
let mut vote_state = VoteState::default(); let mut vote_state = VoteState::default();
for i in 0..1000 { for i in 0..1000 {
@ -279,6 +355,7 @@ mod tests {
let mut stake_account = let mut stake_account =
Account::new(stake_lamports, std::mem::size_of::<StakeState>(), &id()); Account::new(stake_lamports, std::mem::size_of::<StakeState>(), &id());
// unsigned keyed account
let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, false, &mut stake_account); let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, false, &mut stake_account);
{ {
@ -286,50 +363,78 @@ mod tests {
assert_eq!(stake_state, StakeState::default()); assert_eq!(stake_state, StakeState::default());
} }
stake_keyed_account.initialize_stake().unwrap();
assert_eq!( assert_eq!(
stake_keyed_account.delegate_stake(&vote_keyed_account, 0), stake_keyed_account.delegate_stake(&vote_keyed_account, 0, &current),
Err(InstructionError::MissingRequiredSignature) Err(InstructionError::MissingRequiredSignature)
); );
// signed keyed account
let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &mut stake_account); let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &mut stake_account);
assert!(stake_keyed_account assert!(stake_keyed_account
.delegate_stake(&vote_keyed_account, stake_lamports) .delegate_stake(&vote_keyed_account, stake_lamports, &current)
.is_ok()); .is_ok());
// verify that delegate_stake() looks right, compare against hand-rolled
let stake_state: StakeState = stake_keyed_account.state().unwrap();
assert_eq!(
stake_state,
StakeState::Stake(Stake {
voter_pubkey: vote_keypair.pubkey(),
credits_observed: vote_state.credits(),
stake: stake_lamports,
epoch: 0,
prev_stake: 0
})
);
// verify that delegate_stake can't be called twice StakeState::default()
// signed keyed account
assert_eq!(
stake_keyed_account.delegate_stake(&vote_keyed_account, stake_lamports, &current),
Err(InstructionError::InvalidAccountData)
);
// verify can only stake up to account lamports // verify can only stake up to account lamports
let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &mut stake_account); let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &mut stake_account);
assert_eq!( assert_eq!(
stake_keyed_account.delegate_stake(&vote_keyed_account, stake_lamports + 1), stake_keyed_account.delegate_stake(&vote_keyed_account, stake_lamports + 1, &current),
Err(InstructionError::InsufficientFunds) Err(InstructionError::InsufficientFunds)
); );
// verify that create_stake_account() matches the
// resulting account from delegate_stake()
assert_eq!(
create_stake_account(&vote_pubkey, &vote_state, stake_lamports),
*stake_keyed_account.account,
);
let stake_state: StakeState = stake_keyed_account.state().unwrap();
assert_eq!(
stake_state,
StakeState::Stake {
voter_pubkey: vote_keypair.pubkey(),
credits_observed: vote_state.credits(),
stake: stake_lamports,
}
);
let stake_state = StakeState::MiningPool { let stake_state = StakeState::MiningPool {
epoch: 0, epoch: 0,
point_value: 0.0, point_value: 0.0,
}; };
stake_keyed_account.set_state(&stake_state).unwrap(); stake_keyed_account.set_state(&stake_state).unwrap();
assert!(stake_keyed_account assert!(stake_keyed_account
.delegate_stake(&vote_keyed_account, 0) .delegate_stake(&vote_keyed_account, 0, &current)
.is_err()); .is_err());
} }
#[test]
fn test_stake_stake() {
let mut stake = Stake::default();
assert_eq!(stake.stake(0), 0);
let staked = STAKE_WARMUP_COOLDOWN_EPOCHS;
stake.delegate(staked, &Pubkey::default(), &VoteState::default(), 1);
// test warmup
for i in 0..STAKE_WARMUP_COOLDOWN_EPOCHS {
assert_eq!(stake.stake(i), i);
}
assert_eq!(stake.stake(STAKE_WARMUP_COOLDOWN_EPOCHS * 42), staked);
stake.deactivate(STAKE_WARMUP_COOLDOWN_EPOCHS);
// test cooldown
for i in STAKE_WARMUP_COOLDOWN_EPOCHS..STAKE_WARMUP_COOLDOWN_EPOCHS * 2 {
assert_eq!(
stake.stake(i),
staked
- (staked / STAKE_WARMUP_COOLDOWN_EPOCHS) * (i - STAKE_WARMUP_COOLDOWN_EPOCHS)
);
}
assert_eq!(stake.stake(STAKE_WARMUP_COOLDOWN_EPOCHS * 42), 0);
}
#[test] #[test]
fn test_stake_state_calculate_rewards() { fn test_stake_state_calculate_rewards() {
let mut vote_state = VoteState::default(); let mut vote_state = VoteState::default();
@ -380,6 +485,8 @@ mod tests {
#[test] #[test]
fn test_stake_redeem_vote_credits() { fn test_stake_redeem_vote_credits() {
let current = syscall::current::Current::default();
let vote_keypair = Keypair::new(); let vote_keypair = Keypair::new();
let mut vote_state = VoteState::default(); let mut vote_state = VoteState::default();
for i in 0..1000 { for i in 0..1000 {
@ -399,11 +506,10 @@ mod tests {
&id(), &id(),
); );
let mut stake_keyed_account = KeyedAccount::new(&pubkey, true, &mut stake_account); let mut stake_keyed_account = KeyedAccount::new(&pubkey, true, &mut stake_account);
stake_keyed_account.initialize_stake().unwrap();
// delegate the stake // delegate the stake
assert!(stake_keyed_account assert!(stake_keyed_account
.delegate_stake(&vote_keyed_account, STAKE_GETS_PAID_EVERY_VOTE) .delegate_stake(&vote_keyed_account, STAKE_GETS_PAID_EVERY_VOTE, &current)
.is_ok()); .is_ok());
let mut mining_pool_account = Account::new(0, std::mem::size_of::<StakeState>(), &id()); let mut mining_pool_account = Account::new(0, std::mem::size_of::<StakeState>(), &id());
@ -457,6 +563,8 @@ mod tests {
#[test] #[test]
fn test_stake_redeem_vote_credits_vote_errors() { fn test_stake_redeem_vote_credits_vote_errors() {
let current = syscall::current::Current::default();
let vote_keypair = Keypair::new(); let vote_keypair = Keypair::new();
let mut vote_state = VoteState::default(); let mut vote_state = VoteState::default();
for i in 0..1000 { for i in 0..1000 {
@ -474,11 +582,10 @@ mod tests {
let mut stake_account = let mut stake_account =
Account::new(stake_lamports, std::mem::size_of::<StakeState>(), &id()); Account::new(stake_lamports, std::mem::size_of::<StakeState>(), &id());
let mut stake_keyed_account = KeyedAccount::new(&pubkey, true, &mut stake_account); let mut stake_keyed_account = KeyedAccount::new(&pubkey, true, &mut stake_account);
stake_keyed_account.initialize_stake().unwrap();
// delegate the stake // delegate the stake
assert!(stake_keyed_account assert!(stake_keyed_account
.delegate_stake(&vote_keyed_account, stake_lamports) .delegate_stake(&vote_keyed_account, stake_lamports, &current)
.is_ok()); .is_ok());
let mut mining_pool_account = Account::new(0, std::mem::size_of::<StakeState>(), &id()); let mut mining_pool_account = Account::new(0, std::mem::size_of::<StakeState>(), &id());

View File

@ -21,16 +21,17 @@ pub struct Stakes {
impl Stakes { impl Stakes {
// sum the stakes that point to the given voter_pubkey // sum the stakes that point to the given voter_pubkey
fn calculate_stake(&self, voter: &Pubkey) -> u64 { fn calculate_stake(&self, voter_pubkey: &Pubkey) -> u64 {
self.stake_accounts self.stake_accounts
.iter() .iter()
.map(|(_, stake_account)| match StakeState::from(stake_account) { .map(|(_, stake_account)| {
Some(StakeState::Stake { StakeState::stake_from(stake_account).map_or(0, |stake| {
voter_pubkey, if stake.voter_pubkey == *voter_pubkey {
stake, stake.stake
.. } else {
}) if *voter == voter_pubkey => stake, 0
_ => 0, }
})
}) })
.sum() .sum()
} }

View File

@ -659,15 +659,13 @@ fn process_show_stake_account(
use solana_stake_api::stake_state::StakeState; use solana_stake_api::stake_state::StakeState;
let stake_account = rpc_client.get_account(staking_account_pubkey)?; let stake_account = rpc_client.get_account(staking_account_pubkey)?;
match stake_account.state() { match stake_account.state() {
Ok(StakeState::Stake { Ok(StakeState::Stake(stake)) => {
voter_pubkey,
credits_observed,
stake,
}) => {
println!("account lamports: {}", stake_account.lamports); println!("account lamports: {}", stake_account.lamports);
println!("voter pubkey: {}", voter_pubkey); println!("voter pubkey: {}", stake.voter_pubkey);
println!("credits observed: {}", credits_observed); println!("credits observed: {}", stake.credits_observed);
println!("activated stake: {}", stake); println!("epoch: {}", stake.epoch);
println!("activated stake: {}", stake.stake);
println!("previous stake: {}", stake.prev_stake);
Ok("".to_string()) Ok("".to_string())
} }
Ok(StakeState::MiningPool { epoch, point_value }) => { Ok(StakeState::MiningPool { epoch, point_value }) => {