make voter_pubkey a function of epoch (#5830)

* make voter_pubkey a function of epoch

* fixups
This commit is contained in:
Rob Walker 2019-09-09 18:17:32 -07:00 committed by GitHub
parent 7682db4826
commit b881029de3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 189 additions and 152 deletions

View File

@ -6,32 +6,34 @@ use log::*;
use num_traits::FromPrimitive; use num_traits::FromPrimitive;
use serde_json; use serde_json;
use serde_json::{json, Value}; use serde_json::{json, Value};
use solana_budget_api; use solana_budget_api::budget_instruction::{self, BudgetError};
use solana_budget_api::budget_instruction;
use solana_budget_api::budget_state::BudgetError;
use solana_client::client_error::ClientError; use solana_client::client_error::ClientError;
use solana_client::rpc_client::RpcClient; use solana_client::rpc_client::RpcClient;
#[cfg(not(test))] #[cfg(not(test))]
use solana_drone::drone::request_airdrop_transaction; use solana_drone::drone::request_airdrop_transaction;
#[cfg(test)] #[cfg(test)]
use solana_drone::drone_mock::request_airdrop_transaction; use solana_drone::drone_mock::request_airdrop_transaction;
use solana_sdk::account_utils::State; use solana_sdk::{
use solana_sdk::bpf_loader; account_utils::State,
use solana_sdk::fee_calculator::FeeCalculator; bpf_loader,
use solana_sdk::hash::Hash; fee_calculator::FeeCalculator,
use solana_sdk::instruction::InstructionError; hash::Hash,
use solana_sdk::instruction_processor_utils::DecodeError; instruction::InstructionError,
use solana_sdk::loader_instruction; instruction_processor_utils::DecodeError,
use solana_sdk::message::Message; loader_instruction,
use solana_sdk::pubkey::Pubkey; message::Message,
use solana_sdk::signature::{read_keypair, Keypair, KeypairUtil, Signature}; pubkey::Pubkey,
use solana_sdk::system_instruction::SystemError; signature::{read_keypair, Keypair, KeypairUtil, Signature},
use solana_sdk::system_transaction; system_instruction::SystemError,
use solana_sdk::transaction::{Transaction, TransactionError}; system_transaction,
use solana_stake_api::{stake_instruction, stake_state::StakeError}; transaction::{Transaction, TransactionError},
};
use solana_stake_api::stake_instruction::{self, StakeError};
use solana_storage_api::storage_instruction; use solana_storage_api::storage_instruction;
use solana_vote_api::vote_instruction; use solana_vote_api::{
use solana_vote_api::vote_state::VoteState; vote_instruction::{self, VoteError},
vote_state::VoteState,
};
use std::collections::VecDeque; use std::collections::VecDeque;
use std::fs::File; use std::fs::File;
use std::io::{Read, Write}; use std::io::{Read, Write};
@ -617,9 +619,9 @@ fn process_authorize_voter(
recent_blockhash, recent_blockhash,
); );
check_account_for_fee(rpc_client, config, &fee_calculator, &tx.message)?; check_account_for_fee(rpc_client, config, &fee_calculator, &tx.message)?;
let signature_str = rpc_client let result = rpc_client
.send_and_confirm_transaction(&mut tx, &[&config.keypair, &authorized_voter_keypair])?; .send_and_confirm_transaction(&mut tx, &[&config.keypair, &authorized_voter_keypair]);
Ok(signature_str.to_string()) log_instruction_custom_error::<VoteError>(result)
} }
fn process_show_account( fn process_show_account(

View File

@ -1,13 +1,39 @@
use crate::budget_expr::BudgetExpr; use crate::{budget_expr::BudgetExpr, budget_state::BudgetState, id};
use crate::budget_state::BudgetState;
use crate::id;
use bincode::serialized_size; use bincode::serialized_size;
use chrono::prelude::{DateTime, Utc}; use chrono::prelude::{DateTime, Utc};
use num_derive::FromPrimitive;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use solana_sdk::hash::Hash; use solana_sdk::{
use solana_sdk::instruction::{AccountMeta, Instruction}; hash::Hash,
use solana_sdk::pubkey::Pubkey; instruction::{AccountMeta, Instruction},
use solana_sdk::system_instruction; instruction_processor_utils::DecodeError,
pubkey::Pubkey,
system_instruction,
};
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, FromPrimitive)]
pub enum BudgetError {
DestinationMissing,
}
impl<T> DecodeError<T> for BudgetError {
fn type_of() -> &'static str {
"BudgetError"
}
}
impl std::fmt::Display for BudgetError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"{}",
match self {
BudgetError::DestinationMissing => "destination missing",
}
)
}
}
impl std::error::Error for BudgetError {}
/// A smart contract. /// A smart contract.
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]

View File

@ -1,14 +1,15 @@
//! budget program //! budget program
use crate::budget_expr::Witness; use crate::{
use crate::budget_instruction::BudgetInstruction; budget_expr::Witness,
use crate::budget_state::{BudgetError, BudgetState}; budget_instruction::{BudgetError, BudgetInstruction},
budget_state::BudgetState,
};
use bincode::deserialize; use bincode::deserialize;
use chrono::prelude::{DateTime, Utc}; use chrono::prelude::{DateTime, Utc};
use log::*; use log::*;
use solana_sdk::account::KeyedAccount; use solana_sdk::{
use solana_sdk::hash::hash; account::KeyedAccount, hash::hash, instruction::InstructionError, pubkey::Pubkey,
use solana_sdk::instruction::InstructionError; };
use solana_sdk::pubkey::Pubkey;
/// Process a Witness Signature. Any payment plans waiting on this signature /// Process a Witness Signature. Any payment plans waiting on this signature
/// will progress one step. /// will progress one step.

View File

@ -1,28 +1,8 @@
//! budget state //! budget state
use crate::budget_expr::BudgetExpr; use crate::budget_expr::BudgetExpr;
use bincode::{self, deserialize, serialize_into}; use bincode::{self, deserialize, serialize_into};
use num_derive::FromPrimitive;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use solana_sdk::instruction::InstructionError; use solana_sdk::instruction::InstructionError;
use solana_sdk::instruction_processor_utils::DecodeError;
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, FromPrimitive)]
pub enum BudgetError {
DestinationMissing,
}
impl<T> DecodeError<T> for BudgetError {
fn type_of() -> &'static str {
"BudgetError"
}
}
impl std::fmt::Display for BudgetError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "error")
}
}
impl std::error::Error for BudgetError {}
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
pub struct BudgetState { pub struct BudgetState {

View File

@ -4,15 +4,36 @@ use crate::{
}; };
use bincode::deserialize; use bincode::deserialize;
use log::*; use log::*;
use num_derive::{FromPrimitive, ToPrimitive};
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use solana_sdk::{ use solana_sdk::{
account::KeyedAccount, account::KeyedAccount,
clock::Slot, clock::Slot,
instruction::{AccountMeta, Instruction, InstructionError}, instruction::{AccountMeta, Instruction, InstructionError},
instruction_processor_utils::DecodeError,
pubkey::Pubkey, pubkey::Pubkey,
system_instruction, sysvar, system_instruction, sysvar,
}; };
/// Reasons the stake might have had an error
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, FromPrimitive, ToPrimitive)]
pub enum StakeError {
NoCreditsToRedeem,
}
impl<E> DecodeError<E> for StakeError {
fn type_of() -> &'static str {
"StakeError"
}
}
impl std::fmt::Display for StakeError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
StakeError::NoCreditsToRedeem => write!(f, "not enough credits to redeem"),
}
}
}
impl std::error::Error for StakeError {}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum StakeInstruction { pub enum StakeInstruction {
/// `Lockup` a stake until the specified slot /// `Lockup` a stake until the specified slot
@ -454,4 +475,30 @@ mod tests {
); );
} }
#[test]
fn test_custom_error_decode() {
use num_traits::FromPrimitive;
fn pretty_err<T>(err: InstructionError) -> String
where
T: 'static + std::error::Error + DecodeError<T> + FromPrimitive,
{
if let InstructionError::CustomError(code) = err {
let specific_error: T = T::decode_custom_error_to_enum(code).unwrap();
format!(
"{:?}: {}::{:?} - {}",
err,
T::type_of(),
specific_error,
specific_error,
)
} else {
"".to_string()
}
}
assert_eq!(
"CustomError(0): StakeError::NoCreditsToRedeem - not enough credits to redeem",
pretty_err::<StakeError>(StakeError::NoCreditsToRedeem.into())
)
}
} }

View File

@ -3,15 +3,13 @@
//! * keep track of rewards //! * keep track of rewards
//! * own mining pools //! * own mining pools
use crate::{config::Config, id}; use crate::{config::Config, id, stake_instruction::StakeError};
use num_derive::{FromPrimitive, ToPrimitive};
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use solana_sdk::{ use solana_sdk::{
account::{Account, KeyedAccount}, account::{Account, KeyedAccount},
account_utils::State, account_utils::State,
clock::{Epoch, Slot}, clock::{Epoch, Slot},
instruction::InstructionError, instruction::InstructionError,
instruction_processor_utils::DecodeError,
pubkey::Pubkey, pubkey::Pubkey,
sysvar::{ sysvar::{
self, self,
@ -21,6 +19,7 @@ use solana_sdk::{
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)]
#[allow(clippy::large_enum_variant)]
pub enum StakeState { pub enum StakeState {
Uninitialized, Uninitialized,
Lockup(Slot), Lockup(Slot),
@ -52,46 +51,46 @@ impl StakeState {
} }
} }
/// Reasons the stake might have had an error
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, FromPrimitive, ToPrimitive)]
pub enum StakeError {
NoCreditsToRedeem,
}
impl<E> DecodeError<E> for StakeError {
fn type_of() -> &'static str {
"StakeError"
}
}
impl std::fmt::Display for StakeError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
StakeError::NoCreditsToRedeem => write!(f, "not enough credits to redeem"),
}
}
}
impl std::error::Error for StakeError {}
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] #[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
pub struct Stake { pub struct Stake {
/// most recently delegated vote account pubkey
pub voter_pubkey: Pubkey, pub voter_pubkey: Pubkey,
/// the epoch when voter_pubkey was most recently set
pub voter_pubkey_epoch: Epoch,
/// credits observed is credits from vote account state when delegated or redeemed
pub credits_observed: u64, pub credits_observed: u64,
pub stake: u64, // stake amount activated /// activated stake amount, set at delegate_stake() time
pub activation_epoch: Epoch, // epoch the stake was activated, std::Epoch::MAX if is a bootstrap stake pub stake: u64,
pub deactivation_epoch: Epoch, // epoch the stake was deactivated, std::Epoch::MAX if not deactivated /// epoch at which this stake was activated, std::Epoch::MAX if is a bootstrap stake
pub activation_epoch: Epoch,
/// epoch the stake was deactivated, std::Epoch::MAX if not deactivated
pub deactivation_epoch: Epoch,
/// stake config (warmup, etc.)
pub config: Config, pub config: Config,
/// the Slot at which this stake becomes available for withdrawal
pub lockup: Slot, pub lockup: Slot,
/// history of prior delegates and the epoch ranges for which
/// they were set, circular buffer
pub prior_delegates: [(Pubkey, Epoch, Epoch); MAX_PRIOR_DELEGATES],
/// next pointer
pub prior_delegates_idx: usize,
} }
const MAX_PRIOR_DELEGATES: usize = 32;
impl Default for Stake { impl Default for Stake {
fn default() -> Self { fn default() -> Self {
Self { Self {
voter_pubkey: Pubkey::default(), voter_pubkey: Pubkey::default(),
voter_pubkey_epoch: 0,
credits_observed: 0, credits_observed: 0,
stake: 0, stake: 0,
activation_epoch: 0, activation_epoch: 0,
deactivation_epoch: std::u64::MAX, deactivation_epoch: std::u64::MAX,
config: Config::default(), config: Config::default(),
lockup: 0, lockup: 0,
prior_delegates: <[(Pubkey, Epoch, Epoch); MAX_PRIOR_DELEGATES]>::default(),
prior_delegates_idx: 0,
} }
} }
} }
@ -105,6 +104,10 @@ impl Stake {
self.stake_activating_and_deactivating(epoch, history).0 self.stake_activating_and_deactivating(epoch, history).0
} }
pub fn voter_pubkey(&self, _epoch: Epoch) -> &Pubkey {
&self.voter_pubkey
}
fn stake_activating_and_deactivating( fn stake_activating_and_deactivating(
&self, &self,
epoch: Epoch, epoch: Epoch,
@ -405,6 +408,8 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
{ {
let vote_state: VoteState = vote_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.voter_pubkey != *vote_account.unsigned_key() { if stake.voter_pubkey != *vote_account.unsigned_key() {
return Err(InstructionError::InvalidArgument); return Err(InstructionError::InvalidArgument);
} }
@ -633,8 +638,7 @@ mod tests {
stake: stake_lamports, stake: stake_lamports,
activation_epoch: clock.epoch, activation_epoch: clock.epoch,
deactivation_epoch: std::u64::MAX, deactivation_epoch: std::u64::MAX,
config: Config::default(), ..Stake::default()
lockup: 0
}) })
); );
// verify that delegate_stake can't be called twice StakeState::default() // verify that delegate_stake can't be called twice StakeState::default()
@ -1257,32 +1261,6 @@ mod tests {
); );
} }
#[test]
fn test_custom_error_decode() {
use num_traits::FromPrimitive;
fn pretty_err<T>(err: InstructionError) -> String
where
T: 'static + std::error::Error + DecodeError<T> + FromPrimitive,
{
if let InstructionError::CustomError(code) = err {
let specific_error: T = T::decode_custom_error_to_enum(code).unwrap();
format!(
"{:?}: {}::{:?} - {}",
err,
T::type_of(),
specific_error,
specific_error,
)
} else {
"".to_string()
}
}
assert_eq!(
"CustomError(0): StakeError::NoCreditsToRedeem - not enough credits to redeem",
pretty_err::<StakeError>(StakeError::NoCreditsToRedeem.into())
)
}
#[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();

View File

@ -1,17 +1,52 @@
//! Vote program //! Vote program
//! Receive and processes votes from validators //! Receive and processes votes from validators
use crate::id; use crate::{
use crate::vote_state::{self, Vote, VoteState}; id,
vote_state::{self, Vote, VoteState},
};
use bincode::deserialize; use bincode::deserialize;
use log::*; use log::*;
use num_derive::{FromPrimitive, ToPrimitive};
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use solana_metrics::datapoint_warn; use solana_metrics::datapoint_warn;
use solana_sdk::account::KeyedAccount; use solana_sdk::{
use solana_sdk::instruction::{AccountMeta, Instruction, InstructionError}; account::KeyedAccount,
use solana_sdk::pubkey::Pubkey; instruction::{AccountMeta, Instruction, InstructionError},
use solana_sdk::system_instruction; instruction_processor_utils::DecodeError,
use solana_sdk::sysvar; pubkey::Pubkey,
system_instruction, sysvar,
};
/// Reasons the stake might have had an error
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, FromPrimitive, ToPrimitive)]
pub enum VoteError {
VoteTooOld,
SlotsMismatch,
SlotHashMismatch,
EmptySlots,
}
impl<E> DecodeError<E> for VoteError {
fn type_of() -> &'static str {
"VoteError"
}
}
impl std::fmt::Display for VoteError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"{}",
match self {
VoteError::VoteTooOld => "vote already recorded or not in slot hashes history",
VoteError::SlotsMismatch => "vote slots do not match bank history",
VoteError::SlotHashMismatch => "vote hash does not match bank hash",
VoteError::EmptySlots => "vote has no slots, invalid",
}
)
}
}
impl std::error::Error for VoteError {}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum VoteInstruction { pub enum VoteInstruction {

View File

@ -1,9 +1,8 @@
//! Vote state, vote program //! Vote state, vote program
//! Receive and processes votes from validators //! Receive and processes votes from validators
use crate::id; use crate::{id, vote_instruction::VoteError};
use bincode::{deserialize, serialize_into, serialized_size, ErrorKind}; use bincode::{deserialize, serialize_into, serialized_size, ErrorKind};
use log::*; use log::*;
use num_derive::{FromPrimitive, ToPrimitive};
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use solana_sdk::sysvar::slot_hashes::SlotHash; use solana_sdk::sysvar::slot_hashes::SlotHash;
use solana_sdk::{ use solana_sdk::{
@ -12,7 +11,6 @@ use solana_sdk::{
clock::{Epoch, Slot}, clock::{Epoch, Slot},
hash::Hash, hash::Hash,
instruction::InstructionError, instruction::InstructionError,
instruction_processor_utils::DecodeError,
pubkey::Pubkey, pubkey::Pubkey,
sysvar::clock::Clock, sysvar::clock::Clock,
}; };
@ -26,36 +24,6 @@ pub const INITIAL_LOCKOUT: usize = 2;
// smaller numbers makes // smaller numbers makes
pub const MAX_EPOCH_CREDITS_HISTORY: usize = 64; pub const MAX_EPOCH_CREDITS_HISTORY: usize = 64;
/// Reasons the stake might have had an error
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, FromPrimitive, ToPrimitive)]
pub enum VoteError {
VoteTooOld,
SlotsMismatch,
SlotHashMismatch,
EmptySlots,
}
impl<E> DecodeError<E> for VoteError {
fn type_of() -> &'static str {
"VoteError"
}
}
impl std::fmt::Display for VoteError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"{}",
match self {
VoteError::VoteTooOld => "vote already recorded or not in slot hashes history",
VoteError::SlotsMismatch => "vote slots do not match bank history",
VoteError::SlotHashMismatch => "vote hash does not match bank hash",
VoteError::EmptySlots => "vote has no slots, invalid",
}
)
}
}
impl std::error::Error for VoteError {}
#[derive(Serialize, Default, Deserialize, Debug, PartialEq, Eq, Clone)] #[derive(Serialize, Default, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct Vote { pub struct Vote {
/// A stack of votes starting with the oldest vote /// A stack of votes starting with the oldest vote

View File

@ -85,7 +85,7 @@ impl Stakes {
.iter() .iter()
.map(|(_, stake_account)| { .map(|(_, stake_account)| {
StakeState::stake_from(stake_account).map_or(0, |stake| { StakeState::stake_from(stake_account).map_or(0, |stake| {
if stake.voter_pubkey == *voter_pubkey { if stake.voter_pubkey(epoch) == voter_pubkey {
stake.stake(epoch, stake_history) stake.stake(epoch, stake_history)
} else { } else {
0 0
@ -129,7 +129,7 @@ impl Stakes {
let old_stake = self.stake_accounts.get(pubkey).and_then(|old_account| { let old_stake = self.stake_accounts.get(pubkey).and_then(|old_account| {
StakeState::stake_from(old_account).map(|stake| { StakeState::stake_from(old_account).map(|stake| {
( (
stake.voter_pubkey, *stake.voter_pubkey(self.epoch),
stake.stake(self.epoch, Some(&self.stake_history)), stake.stake(self.epoch, Some(&self.stake_history)),
) )
}) })
@ -137,7 +137,7 @@ impl Stakes {
let stake = StakeState::stake_from(account).map(|stake| { let stake = StakeState::stake_from(account).map(|stake| {
( (
stake.voter_pubkey, *stake.voter_pubkey(self.epoch),
if account.lamports != 0 { if account.lamports != 0 {
stake.stake(self.epoch, Some(&self.stake_history)) stake.stake(self.epoch, Some(&self.stake_history))
} else { } else {