add stake and vote errors (#5814)
* add stake errors * remove self from type_of * sheesh * better * add stake errors * update wallet error handling * fixup
This commit is contained in:
parent
e5f902369c
commit
4a20c2aa1b
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -3739,6 +3739,8 @@ version = "0.19.0-pre0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"bincode 1.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bincode 1.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"num-derive 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_derive 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_derive 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -3866,6 +3868,8 @@ version = "0.19.0-pre0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"bincode 1.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bincode 1.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"num-derive 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_derive 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_derive 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"solana-logger 0.19.0-pre0",
|
"solana-logger 0.19.0-pre0",
|
||||||
|
@ -28,7 +28,7 @@ use solana_sdk::signature::{read_keypair, Keypair, KeypairUtil, Signature};
|
|||||||
use solana_sdk::system_instruction::SystemError;
|
use solana_sdk::system_instruction::SystemError;
|
||||||
use solana_sdk::system_transaction;
|
use solana_sdk::system_transaction;
|
||||||
use solana_sdk::transaction::{Transaction, TransactionError};
|
use solana_sdk::transaction::{Transaction, TransactionError};
|
||||||
use solana_stake_api::stake_instruction;
|
use solana_stake_api::{stake_instruction, stake_state::StakeError};
|
||||||
use solana_storage_api::storage_instruction;
|
use solana_storage_api::storage_instruction;
|
||||||
use solana_vote_api::vote_instruction;
|
use solana_vote_api::vote_instruction;
|
||||||
use solana_vote_api::vote_state::VoteState;
|
use solana_vote_api::vote_state::VoteState;
|
||||||
@ -586,8 +586,7 @@ fn process_create_vote_account(
|
|||||||
let mut tx = Transaction::new_signed_instructions(&[&config.keypair], ixs, recent_blockhash);
|
let mut tx = Transaction::new_signed_instructions(&[&config.keypair], ixs, 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 result = rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair]);
|
let result = rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair]);
|
||||||
let signature_str = log_instruction_custom_error::<SystemError>(result)?;
|
log_instruction_custom_error::<SystemError>(result)
|
||||||
Ok(signature_str.to_string())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_authorize_voter(
|
fn process_authorize_voter(
|
||||||
@ -733,9 +732,9 @@ fn process_deactivate_stake_account(
|
|||||||
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, &stake_account_keypair])?;
|
.send_and_confirm_transaction(&mut tx, &[&config.keypair, &stake_account_keypair]);
|
||||||
Ok(signature_str.to_string())
|
log_instruction_custom_error::<StakeError>(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_delegate_stake(
|
fn process_delegate_stake(
|
||||||
@ -812,8 +811,7 @@ fn process_delegate_stake(
|
|||||||
|
|
||||||
let result = rpc_client
|
let result = rpc_client
|
||||||
.send_and_confirm_transaction(&mut tx, &[&config.keypair, &stake_account_keypair]);
|
.send_and_confirm_transaction(&mut tx, &[&config.keypair, &stake_account_keypair]);
|
||||||
let signature_str = log_instruction_custom_error::<SystemError>(result)?;
|
log_instruction_custom_error::<StakeError>(result)
|
||||||
Ok(signature_str.to_string())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_withdraw_stake(
|
fn process_withdraw_stake(
|
||||||
@ -838,9 +836,9 @@ fn process_withdraw_stake(
|
|||||||
);
|
);
|
||||||
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, &stake_account_keypair])?;
|
.send_and_confirm_transaction(&mut tx, &[&config.keypair, &stake_account_keypair]);
|
||||||
Ok(signature_str.to_string())
|
log_instruction_custom_error::<StakeError>(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_redeem_vote_credits(
|
fn process_redeem_vote_credits(
|
||||||
@ -861,8 +859,8 @@ fn process_redeem_vote_credits(
|
|||||||
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.send_and_confirm_transaction(&mut tx, &[&config.keypair])?;
|
let result = rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair]);
|
||||||
Ok(signature_str.to_string())
|
log_instruction_custom_error::<StakeError>(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_show_stake_account(
|
fn process_show_stake_account(
|
||||||
@ -931,8 +929,7 @@ fn process_create_replicator_storage_account(
|
|||||||
let mut tx = Transaction::new_signed_instructions(&[&config.keypair], ixs, recent_blockhash);
|
let mut tx = Transaction::new_signed_instructions(&[&config.keypair], ixs, 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 result = rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair]);
|
let result = rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair]);
|
||||||
let signature_str = log_instruction_custom_error::<SystemError>(result)?;
|
log_instruction_custom_error::<SystemError>(result)
|
||||||
Ok(signature_str.to_string())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_create_validator_storage_account(
|
fn process_create_validator_storage_account(
|
||||||
@ -958,8 +955,7 @@ fn process_create_validator_storage_account(
|
|||||||
let mut tx = Transaction::new_signed_instructions(&[&config.keypair], ixs, recent_blockhash);
|
let mut tx = Transaction::new_signed_instructions(&[&config.keypair], ixs, 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 result = rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair]);
|
let result = rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair]);
|
||||||
let signature_str = log_instruction_custom_error::<SystemError>(result)?;
|
log_instruction_custom_error::<SystemError>(result)
|
||||||
Ok(signature_str.to_string())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_claim_storage_reward(
|
fn process_claim_storage_reward(
|
||||||
@ -1104,8 +1100,7 @@ fn process_pay(
|
|||||||
let mut tx = system_transaction::transfer(&config.keypair, to, lamports, blockhash);
|
let mut tx = system_transaction::transfer(&config.keypair, to, lamports, blockhash);
|
||||||
check_account_for_fee(rpc_client, config, &fee_calculator, &tx.message)?;
|
check_account_for_fee(rpc_client, config, &fee_calculator, &tx.message)?;
|
||||||
let result = rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair]);
|
let result = rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair]);
|
||||||
let signature_str = log_instruction_custom_error::<SystemError>(result)?;
|
log_instruction_custom_error::<SystemError>(result)
|
||||||
Ok(signature_str.to_string())
|
|
||||||
} else if *witnesses == None {
|
} else if *witnesses == None {
|
||||||
let dt = timestamp.unwrap();
|
let dt = timestamp.unwrap();
|
||||||
let dt_pubkey = match timestamp_pubkey {
|
let dt_pubkey = match timestamp_pubkey {
|
||||||
@ -1182,8 +1177,7 @@ fn process_cancel(rpc_client: &RpcClient, config: &WalletConfig, pubkey: &Pubkey
|
|||||||
let mut tx = Transaction::new_signed_instructions(&[&config.keypair], vec![ix], blockhash);
|
let mut tx = Transaction::new_signed_instructions(&[&config.keypair], vec![ix], blockhash);
|
||||||
check_account_for_fee(rpc_client, config, &fee_calculator, &tx.message)?;
|
check_account_for_fee(rpc_client, config, &fee_calculator, &tx.message)?;
|
||||||
let result = rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair]);
|
let result = rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair]);
|
||||||
let signature_str = log_instruction_custom_error::<BudgetError>(result)?;
|
log_instruction_custom_error::<BudgetError>(result)
|
||||||
Ok(signature_str.to_string())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_get_slot(rpc_client: &RpcClient) -> ProcessResult {
|
fn process_get_slot(rpc_client: &RpcClient) -> ProcessResult {
|
||||||
@ -1209,9 +1203,7 @@ fn process_time_elapsed(
|
|||||||
let mut tx = Transaction::new_signed_instructions(&[&config.keypair], vec![ix], blockhash);
|
let mut tx = Transaction::new_signed_instructions(&[&config.keypair], vec![ix], blockhash);
|
||||||
check_account_for_fee(rpc_client, config, &fee_calculator, &tx.message)?;
|
check_account_for_fee(rpc_client, config, &fee_calculator, &tx.message)?;
|
||||||
let result = rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair]);
|
let result = rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair]);
|
||||||
let signature_str = log_instruction_custom_error::<BudgetError>(result)?;
|
log_instruction_custom_error::<BudgetError>(result)
|
||||||
|
|
||||||
Ok(signature_str.to_string())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_witness(
|
fn process_witness(
|
||||||
@ -1226,9 +1218,7 @@ fn process_witness(
|
|||||||
let mut tx = Transaction::new_signed_instructions(&[&config.keypair], vec![ix], blockhash);
|
let mut tx = Transaction::new_signed_instructions(&[&config.keypair], vec![ix], blockhash);
|
||||||
check_account_for_fee(rpc_client, config, &fee_calculator, &tx.message)?;
|
check_account_for_fee(rpc_client, config, &fee_calculator, &tx.message)?;
|
||||||
let result = rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair]);
|
let result = rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair]);
|
||||||
let signature_str = log_instruction_custom_error::<BudgetError>(result)?;
|
log_instruction_custom_error::<BudgetError>(result)
|
||||||
|
|
||||||
Ok(signature_str.to_string())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_get_version(rpc_client: &RpcClient, config: &WalletConfig) -> ProcessResult {
|
fn process_get_version(rpc_client: &RpcClient, config: &WalletConfig) -> ProcessResult {
|
||||||
@ -1634,7 +1624,7 @@ pub fn request_and_confirm_airdrop(
|
|||||||
drone_addr: &SocketAddr,
|
drone_addr: &SocketAddr,
|
||||||
to_pubkey: &Pubkey,
|
to_pubkey: &Pubkey,
|
||||||
lamports: u64,
|
lamports: u64,
|
||||||
) -> Result<(), Box<dyn error::Error>> {
|
) -> ProcessResult {
|
||||||
let (blockhash, _fee_calculator) = rpc_client.get_recent_blockhash()?;
|
let (blockhash, _fee_calculator) = rpc_client.get_recent_blockhash()?;
|
||||||
let keypair = {
|
let keypair = {
|
||||||
let mut retries = 5;
|
let mut retries = 5;
|
||||||
@ -1649,8 +1639,7 @@ pub fn request_and_confirm_airdrop(
|
|||||||
}?;
|
}?;
|
||||||
let mut tx = keypair.airdrop_transaction();
|
let mut tx = keypair.airdrop_transaction();
|
||||||
let result = rpc_client.send_and_confirm_transaction(&mut tx, &[&keypair]);
|
let result = rpc_client.send_and_confirm_transaction(&mut tx, &[&keypair]);
|
||||||
log_instruction_custom_error::<SystemError>(result)?;
|
log_instruction_custom_error::<SystemError>(result)
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn log_instruction_custom_error<E>(result: Result<String, ClientError>) -> ProcessResult
|
fn log_instruction_custom_error<E>(result: Result<String, ClientError>) -> ProcessResult
|
||||||
@ -1665,12 +1654,7 @@ where
|
|||||||
)) = err
|
)) = err
|
||||||
{
|
{
|
||||||
if let Some(specific_error) = E::decode_custom_error_to_enum(code) {
|
if let Some(specific_error) = E::decode_custom_error_to_enum(code) {
|
||||||
error!(
|
error!("{}::{:?}", E::type_of(), specific_error);
|
||||||
"{:?}: {}::{:?}",
|
|
||||||
err,
|
|
||||||
specific_error.type_of(),
|
|
||||||
specific_error
|
|
||||||
);
|
|
||||||
Err(specific_error)?
|
Err(specific_error)?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ pub enum BudgetError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T> DecodeError<T> for BudgetError {
|
impl<T> DecodeError<T> for BudgetError {
|
||||||
fn type_of(&self) -> &'static str {
|
fn type_of() -> &'static str {
|
||||||
"BudgetError"
|
"BudgetError"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,8 @@ edition = "2018"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
bincode = "1.1.4"
|
bincode = "1.1.4"
|
||||||
log = "0.4.8"
|
log = "0.4.8"
|
||||||
|
num-derive = "0.2"
|
||||||
|
num-traits = "0.2"
|
||||||
rand = "0.6.5"
|
rand = "0.6.5"
|
||||||
serde = "1.0.99"
|
serde = "1.0.99"
|
||||||
serde_derive = "1.0.99"
|
serde_derive = "1.0.99"
|
||||||
|
@ -4,11 +4,13 @@
|
|||||||
//! * own mining pools
|
//! * own mining pools
|
||||||
|
|
||||||
use crate::{config::Config, id};
|
use crate::{config::Config, id};
|
||||||
|
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,
|
||||||
instruction::InstructionError,
|
instruction::InstructionError,
|
||||||
|
instruction_processor_utils::DecodeError,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
sysvar::{
|
sysvar::{
|
||||||
self,
|
self,
|
||||||
@ -50,6 +52,25 @@ 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 {
|
||||||
pub voter_pubkey: Pubkey,
|
pub voter_pubkey: Pubkey,
|
||||||
@ -408,7 +429,7 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
|
|||||||
self.set_state(&StakeState::Stake(stake))
|
self.set_state(&StakeState::Stake(stake))
|
||||||
} else {
|
} else {
|
||||||
// not worth collecting
|
// not worth collecting
|
||||||
Err(InstructionError::CustomError(1))
|
Err(StakeError::NoCreditsToRedeem.into())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(InstructionError::InvalidAccountData)
|
Err(InstructionError::InvalidAccountData)
|
||||||
@ -1236,6 +1257,32 @@ 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();
|
||||||
@ -1379,7 +1426,7 @@ mod tests {
|
|||||||
&rewards,
|
&rewards,
|
||||||
&stake_history,
|
&stake_history,
|
||||||
),
|
),
|
||||||
Err(InstructionError::CustomError(1))
|
Err(StakeError::NoCreditsToRedeem.into())
|
||||||
);
|
);
|
||||||
|
|
||||||
// in this call, we've swapped rewards and vote, deserialization of rewards_pool fails
|
// in this call, we've swapped rewards and vote, deserialization of rewards_pool fails
|
||||||
|
@ -13,7 +13,7 @@ pub enum TokenError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T> DecodeError<T> for TokenError {
|
impl<T> DecodeError<T> for TokenError {
|
||||||
fn type_of(&self) -> &'static str {
|
fn type_of() -> &'static str {
|
||||||
"TokenError"
|
"TokenError"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,8 @@ edition = "2018"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
bincode = "1.1.4"
|
bincode = "1.1.4"
|
||||||
log = "0.4.8"
|
log = "0.4.8"
|
||||||
|
num-derive = "0.2"
|
||||||
|
num-traits = "0.2"
|
||||||
serde = "1.0.99"
|
serde = "1.0.99"
|
||||||
serde_derive = "1.0.99"
|
serde_derive = "1.0.99"
|
||||||
solana-logger = { path = "../../logger", version = "0.19.0-pre0" }
|
solana-logger = { path = "../../logger", version = "0.19.0-pre0" }
|
||||||
|
@ -3,14 +3,18 @@
|
|||||||
use crate::id;
|
use crate::id;
|
||||||
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::account::{Account, KeyedAccount};
|
use solana_sdk::{
|
||||||
use solana_sdk::account_utils::State;
|
account::{Account, KeyedAccount},
|
||||||
use solana_sdk::hash::Hash;
|
account_utils::State,
|
||||||
use solana_sdk::instruction::InstructionError;
|
hash::Hash,
|
||||||
use solana_sdk::pubkey::Pubkey;
|
instruction::InstructionError,
|
||||||
use solana_sdk::sysvar::clock::Clock;
|
instruction_processor_utils::DecodeError,
|
||||||
pub use solana_sdk::timing::{Epoch, Slot};
|
pubkey::Pubkey,
|
||||||
|
sysvar::clock::Clock,
|
||||||
|
timing::{Epoch, Slot},
|
||||||
|
};
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
// Maximum number of votes to keep around
|
// Maximum number of votes to keep around
|
||||||
@ -21,6 +25,36 @@ 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
|
||||||
@ -147,10 +181,15 @@ impl VoteState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn check_slots_are_valid(&self, vote: &Vote, slot_hashes: &[(Slot, Hash)]) -> bool {
|
fn check_slots_are_valid(
|
||||||
let mut i = 0;
|
&self,
|
||||||
let mut j = slot_hashes.len();
|
vote: &Vote,
|
||||||
|
slot_hashes: &[(Slot, Hash)],
|
||||||
|
) -> Result<(), VoteError> {
|
||||||
|
let mut i = 0; // index into the vote's slots
|
||||||
|
let mut j = slot_hashes.len(); // index into the slot_hashes
|
||||||
while i < vote.slots.len() && j > 0 {
|
while i < vote.slots.len() && j > 0 {
|
||||||
|
// find the most recent "new" slot in the vote
|
||||||
if self
|
if self
|
||||||
.votes
|
.votes
|
||||||
.back()
|
.back()
|
||||||
@ -168,36 +207,40 @@ impl VoteState {
|
|||||||
}
|
}
|
||||||
if j == slot_hashes.len() {
|
if j == slot_hashes.len() {
|
||||||
warn!(
|
warn!(
|
||||||
"{} dropped vote {:?} to old: {:?} ",
|
"{} dropped vote {:?} too old: {:?} ",
|
||||||
self.node_pubkey, vote, slot_hashes
|
self.node_pubkey, vote, slot_hashes
|
||||||
);
|
);
|
||||||
return false;
|
return Err(VoteError::VoteTooOld);
|
||||||
}
|
}
|
||||||
if i != vote.slots.len() {
|
if i != vote.slots.len() {
|
||||||
warn!(
|
warn!(
|
||||||
"{} dropped vote {:?} failed to match slot: {:?}",
|
"{} dropped vote {:?} failed to match slot: {:?}",
|
||||||
self.node_pubkey, vote, slot_hashes,
|
self.node_pubkey, vote, slot_hashes,
|
||||||
);
|
);
|
||||||
|
return Err(VoteError::SlotsMismatch);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
if slot_hashes[j].1 != vote.hash {
|
if slot_hashes[j].1 != vote.hash {
|
||||||
warn!(
|
warn!(
|
||||||
"{} dropped vote {:?} failed to match hash {} {}",
|
"{} dropped vote {:?} failed to match hash {} {}",
|
||||||
self.node_pubkey, vote, vote.hash, slot_hashes[j].1
|
self.node_pubkey, vote, vote.hash, slot_hashes[j].1
|
||||||
);
|
);
|
||||||
return false;
|
return Err(VoteError::SlotHashMismatch);
|
||||||
}
|
}
|
||||||
true
|
Ok(())
|
||||||
}
|
}
|
||||||
pub fn process_vote(&mut self, vote: &Vote, slot_hashes: &[(Slot, Hash)], epoch: Epoch) {
|
pub fn process_vote(
|
||||||
|
&mut self,
|
||||||
|
vote: &Vote,
|
||||||
|
slot_hashes: &[(Slot, Hash)],
|
||||||
|
epoch: Epoch,
|
||||||
|
) -> Result<(), VoteError> {
|
||||||
if vote.slots.is_empty() {
|
if vote.slots.is_empty() {
|
||||||
return;
|
return Err(VoteError::EmptySlots);
|
||||||
}
|
|
||||||
if !self.check_slots_are_valid(vote, slot_hashes) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
self.check_slots_are_valid(vote, slot_hashes)?;
|
||||||
|
|
||||||
vote.slots.iter().for_each(|s| self.process_slot(*s, epoch));
|
vote.slots.iter().for_each(|s| self.process_slot(*s, epoch));
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_slot(&mut self, slot: Slot, epoch: Epoch) {
|
pub fn process_slot(&mut self, slot: Slot, epoch: Epoch) {
|
||||||
@ -249,7 +292,7 @@ impl VoteState {
|
|||||||
/// "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();
|
||||||
self.process_vote(vote, &slot_hashes, self.epoch);
|
let _ignored = self.process_vote(vote, &slot_hashes, self.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()));
|
||||||
@ -272,7 +315,9 @@ impl VoteState {
|
|||||||
|
|
||||||
/// 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,
|
||||||
/// starting from credits observed.
|
/// starting from credits observed.
|
||||||
/// Each tuple of (Epoch, u64) is the credits() delta as of the end of the Epoch
|
/// Each tuple of (Epoch, u64, u64) is read as (epoch, credits, prev_credits), where
|
||||||
|
/// credits for each epoch is credits - prev_credits; while redundant this makes
|
||||||
|
/// calculating rewards over partial epochs nice and simple
|
||||||
pub fn epoch_credits(&self) -> impl Iterator<Item = &(Epoch, u64, u64)> {
|
pub fn epoch_credits(&self) -> impl Iterator<Item = &(Epoch, u64, u64)> {
|
||||||
self.epoch_credits.iter()
|
self.epoch_credits.iter()
|
||||||
}
|
}
|
||||||
@ -383,7 +428,7 @@ pub fn process_vote(
|
|||||||
return Err(InstructionError::MissingRequiredSignature);
|
return Err(InstructionError::MissingRequiredSignature);
|
||||||
}
|
}
|
||||||
|
|
||||||
vote_state.process_vote(vote, slot_hashes, clock.epoch);
|
vote_state.process_vote(vote, slot_hashes, clock.epoch)?;
|
||||||
vote_account.set_state(&vote_state)
|
vote_account.set_state(&vote_state)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -516,25 +561,28 @@ mod tests {
|
|||||||
let vote = Vote::new(vec![0], hash);
|
let vote = Vote::new(vec![0], hash);
|
||||||
|
|
||||||
// wrong hash
|
// wrong hash
|
||||||
let vote_state = simulate_process_vote(
|
assert_eq!(
|
||||||
&vote_pubkey,
|
simulate_process_vote(
|
||||||
&mut vote_account,
|
&vote_pubkey,
|
||||||
&vote,
|
&mut vote_account,
|
||||||
&[(0, Hash::default())],
|
&vote,
|
||||||
0,
|
&[(0, Hash::default())],
|
||||||
)
|
0,
|
||||||
.unwrap();
|
),
|
||||||
assert_eq!(vote_state.votes.len(), 0);
|
Err(VoteError::SlotHashMismatch.into())
|
||||||
|
);
|
||||||
|
|
||||||
// wrong slot
|
// wrong slot
|
||||||
let vote_state =
|
assert_eq!(
|
||||||
simulate_process_vote(&vote_pubkey, &mut vote_account, &vote, &[(1, hash)], 0).unwrap();
|
simulate_process_vote(&vote_pubkey, &mut vote_account, &vote, &[(1, hash)], 0),
|
||||||
assert_eq!(vote_state.votes.len(), 0);
|
Err(VoteError::SlotsMismatch.into())
|
||||||
|
);
|
||||||
|
|
||||||
// empty slot_hashes
|
// empty slot_hashes
|
||||||
let vote_state =
|
assert_eq!(
|
||||||
simulate_process_vote(&vote_pubkey, &mut vote_account, &vote, &[], 0).unwrap();
|
simulate_process_vote(&vote_pubkey, &mut vote_account, &vote, &[], 0),
|
||||||
assert_eq!(vote_state.votes.len(), 0);
|
Err(VoteError::VoteTooOld.into())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -797,8 +845,8 @@ mod tests {
|
|||||||
let vote = Vote::new(slots, Hash::default());
|
let vote = Vote::new(slots, Hash::default());
|
||||||
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();
|
||||||
|
|
||||||
vote_state_a.process_vote(&vote, &slot_hashes, 0);
|
assert_eq!(vote_state_a.process_vote(&vote, &slot_hashes, 0), Ok(()));
|
||||||
vote_state_b.process_vote(&vote, &slot_hashes, 0);
|
assert_eq!(vote_state_b.process_vote(&vote, &slot_hashes, 0), Ok(()));
|
||||||
assert_eq!(recent_votes(&vote_state_a), recent_votes(&vote_state_b));
|
assert_eq!(recent_votes(&vote_state_a), recent_votes(&vote_state_b));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -807,9 +855,14 @@ mod tests {
|
|||||||
let mut vote_state = VoteState::new(&Pubkey::default(), &Pubkey::default(), 0);
|
let mut vote_state = VoteState::new(&Pubkey::default(), &Pubkey::default(), 0);
|
||||||
|
|
||||||
let vote = Vote::new(vec![0], Hash::default());
|
let vote = Vote::new(vec![0], Hash::default());
|
||||||
let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap() + 1, vote.hash)];
|
let slot_hashes: Vec<_> = vec![(0, vote.hash)];
|
||||||
vote_state.process_vote(&vote, &slot_hashes, 0);
|
assert_eq!(vote_state.process_vote(&vote, &slot_hashes, 0), Ok(()));
|
||||||
assert!(recent_votes(&vote_state).is_empty());
|
let recent = recent_votes(&vote_state);
|
||||||
|
assert_eq!(
|
||||||
|
vote_state.process_vote(&vote, &slot_hashes, 0),
|
||||||
|
Err(VoteError::VoteTooOld)
|
||||||
|
);
|
||||||
|
assert_eq!(recent, recent_votes(&vote_state));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -817,7 +870,10 @@ mod tests {
|
|||||||
let vote_state = VoteState::new(&Pubkey::default(), &Pubkey::default(), 0);
|
let vote_state = VoteState::new(&Pubkey::default(), &Pubkey::default(), 0);
|
||||||
|
|
||||||
let vote = Vote::new(vec![0], Hash::default());
|
let vote = Vote::new(vec![0], Hash::default());
|
||||||
assert_eq!(vote_state.check_slots_are_valid(&vote, &vec![]), false);
|
assert_eq!(
|
||||||
|
vote_state.check_slots_are_valid(&vote, &vec![]),
|
||||||
|
Err(VoteError::VoteTooOld)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -826,7 +882,10 @@ mod tests {
|
|||||||
|
|
||||||
let vote = Vote::new(vec![0], Hash::default());
|
let vote = Vote::new(vec![0], Hash::default());
|
||||||
let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
|
let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
|
||||||
assert_eq!(vote_state.check_slots_are_valid(&vote, &slot_hashes), true);
|
assert_eq!(
|
||||||
|
vote_state.check_slots_are_valid(&vote, &slot_hashes),
|
||||||
|
Ok(())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -835,7 +894,10 @@ mod tests {
|
|||||||
|
|
||||||
let vote = Vote::new(vec![0], Hash::default());
|
let vote = Vote::new(vec![0], Hash::default());
|
||||||
let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), hash(vote.hash.as_ref()))];
|
let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), hash(vote.hash.as_ref()))];
|
||||||
assert_eq!(vote_state.check_slots_are_valid(&vote, &slot_hashes), false);
|
assert_eq!(
|
||||||
|
vote_state.check_slots_are_valid(&vote, &slot_hashes),
|
||||||
|
Err(VoteError::SlotHashMismatch)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -844,7 +906,10 @@ mod tests {
|
|||||||
|
|
||||||
let vote = Vote::new(vec![1], Hash::default());
|
let vote = Vote::new(vec![1], Hash::default());
|
||||||
let slot_hashes: Vec<_> = vec![(0, vote.hash)];
|
let slot_hashes: Vec<_> = vec![(0, vote.hash)];
|
||||||
assert_eq!(vote_state.check_slots_are_valid(&vote, &slot_hashes), false);
|
assert_eq!(
|
||||||
|
vote_state.check_slots_are_valid(&vote, &slot_hashes),
|
||||||
|
Err(VoteError::SlotsMismatch)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -853,8 +918,11 @@ mod tests {
|
|||||||
|
|
||||||
let vote = Vote::new(vec![0], Hash::default());
|
let vote = Vote::new(vec![0], Hash::default());
|
||||||
let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
|
let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
|
||||||
vote_state.process_vote(&vote, &slot_hashes, 0);
|
assert_eq!(vote_state.process_vote(&vote, &slot_hashes, 0), Ok(()));
|
||||||
assert_eq!(vote_state.check_slots_are_valid(&vote, &slot_hashes), false);
|
assert_eq!(
|
||||||
|
vote_state.check_slots_are_valid(&vote, &slot_hashes),
|
||||||
|
Err(VoteError::VoteTooOld)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -863,11 +931,14 @@ mod tests {
|
|||||||
|
|
||||||
let vote = Vote::new(vec![0], Hash::default());
|
let vote = Vote::new(vec![0], Hash::default());
|
||||||
let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
|
let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
|
||||||
vote_state.process_vote(&vote, &slot_hashes, 0);
|
assert_eq!(vote_state.process_vote(&vote, &slot_hashes, 0), Ok(()));
|
||||||
|
|
||||||
let vote = Vote::new(vec![0, 1], Hash::default());
|
let vote = Vote::new(vec![0, 1], Hash::default());
|
||||||
let slot_hashes: Vec<_> = vec![(1, vote.hash), (0, vote.hash)];
|
let slot_hashes: Vec<_> = vec![(1, vote.hash), (0, vote.hash)];
|
||||||
assert_eq!(vote_state.check_slots_are_valid(&vote, &slot_hashes), true);
|
assert_eq!(
|
||||||
|
vote_state.check_slots_are_valid(&vote, &slot_hashes),
|
||||||
|
Ok(())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -876,11 +947,14 @@ mod tests {
|
|||||||
|
|
||||||
let vote = Vote::new(vec![0], Hash::default());
|
let vote = Vote::new(vec![0], Hash::default());
|
||||||
let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
|
let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
|
||||||
vote_state.process_vote(&vote, &slot_hashes, 0);
|
assert_eq!(vote_state.process_vote(&vote, &slot_hashes, 0), Ok(()));
|
||||||
|
|
||||||
let vote = Vote::new(vec![1], Hash::default());
|
let vote = Vote::new(vec![1], Hash::default());
|
||||||
let slot_hashes: Vec<_> = vec![(1, vote.hash), (0, vote.hash)];
|
let slot_hashes: Vec<_> = vec![(1, vote.hash), (0, vote.hash)];
|
||||||
assert_eq!(vote_state.check_slots_are_valid(&vote, &slot_hashes), true);
|
assert_eq!(
|
||||||
|
vote_state.check_slots_are_valid(&vote, &slot_hashes),
|
||||||
|
Ok(())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::account::KeyedAccount;
|
use crate::account::KeyedAccount;
|
||||||
use crate::instruction::InstructionError;
|
use crate::instruction::InstructionError;
|
||||||
use crate::pubkey::Pubkey;
|
use crate::pubkey::Pubkey;
|
||||||
use num_traits::FromPrimitive;
|
use num_traits::{FromPrimitive, ToPrimitive};
|
||||||
|
|
||||||
// All native programs export a symbol named process()
|
// All native programs export a symbol named process()
|
||||||
pub const ENTRYPOINT: &str = "process";
|
pub const ENTRYPOINT: &str = "process";
|
||||||
@ -29,14 +29,23 @@ macro_rules! solana_entrypoint(
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
impl<T> From<T> for InstructionError
|
||||||
|
where
|
||||||
|
T: ToPrimitive,
|
||||||
|
{
|
||||||
|
fn from(error: T) -> Self {
|
||||||
|
InstructionError::CustomError(error.to_u32().unwrap_or(0xbad_c0de))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait DecodeError<E> {
|
pub trait DecodeError<E> {
|
||||||
fn decode_custom_error_to_enum(int: u32) -> Option<E>
|
fn decode_custom_error_to_enum(custom: u32) -> Option<E>
|
||||||
where
|
where
|
||||||
E: FromPrimitive,
|
E: FromPrimitive,
|
||||||
{
|
{
|
||||||
E::from_u32(int)
|
E::from_u32(custom)
|
||||||
}
|
}
|
||||||
fn type_of(&self) -> &'static str;
|
fn type_of() -> &'static str;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -53,7 +62,7 @@ mod tests {
|
|||||||
C,
|
C,
|
||||||
}
|
}
|
||||||
impl<T> DecodeError<T> for TestEnum {
|
impl<T> DecodeError<T> for TestEnum {
|
||||||
fn type_of(&self) -> &'static str {
|
fn type_of() -> &'static str {
|
||||||
"TestEnum"
|
"TestEnum"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ pub enum SystemError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T> DecodeError<T> for SystemError {
|
impl<T> DecodeError<T> for SystemError {
|
||||||
fn type_of(&self) -> &'static str {
|
fn type_of() -> &'static str {
|
||||||
"SystemError"
|
"SystemError"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user