Skip to content

Commit

Permalink
Reject close of active vote accounts (solana-labs#22651)
Browse files Browse the repository at this point in the history
* 10461 Reject close of vote accounts unless it earned no credits in the previous epoch. This is checked by comparing current epoch (from clock sysvar) with the most recent epoch with credits in vote state.
  • Loading branch information
willhickey authored and jeffwashington committed Feb 24, 2022
1 parent 519ca31 commit 9403e2f
Show file tree
Hide file tree
Showing 8 changed files with 385 additions and 17 deletions.
19 changes: 18 additions & 1 deletion programs/vote/src/vote_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,24 @@ pub fn process_instruction(
} else {
None
};
vote_state::withdraw(me, lamports, to, &signers, rent_sysvar.as_deref())

let clock_if_feature_active = if invoke_context
.feature_set
.is_active(&feature_set::reject_vote_account_close_unless_zero_credit_epoch::id())
{
Some(invoke_context.get_sysvar_cache().get_clock()?)
} else {
None
};

vote_state::withdraw(
me,
lamports,
to,
&signers,
rent_sysvar.as_deref(),
clock_if_feature_active.as_deref(),
)
}
VoteInstruction::AuthorizeChecked(vote_authorize) => {
if invoke_context
Expand Down
359 changes: 344 additions & 15 deletions programs/vote/src/vote_state/mod.rs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion runtime/src/bank.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ impl RentDebits {
}

type BankStatusCache = StatusCache<Result<()>>;
#[frozen_abi(digest = "FPLuTUU5MjwsijzDubxY6BvBEkWULhYNUyY6Puqejb4g")]
#[frozen_abi(digest = "6XkxpmzmKZguLZMS1KmU7N2dAcv8MmNhyobJCwRLkTdi")]
pub type BankSlotDelta = SlotDelta<Result<()>>;

// Eager rent collection repeats in cyclic manner.
Expand Down
4 changes: 4 additions & 0 deletions sdk/program/src/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,10 @@ pub enum InstructionError {
/// Accounts data budget exceeded
#[error("Requested account data allocation exceeded the accounts data budget")]
AccountsDataBudgetExceeded,

/// Active vote account close
#[error("Cannot close vote account unless it stopped voting at least one full epoch ago")]
ActiveVoteAccountClose,
// Note: For any new error added here an equivalent ProgramError and its
// conversions must also be added
}
Expand Down
8 changes: 8 additions & 0 deletions sdk/program/src/program_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ pub enum ProgramError {
IllegalOwner,
#[error("Requested account data allocation exceeded the accounts data budget")]
AccountsDataBudgetExceeded,
#[error("Cannot close vote account unless it stopped voting at least one full epoch ago")]
ActiveVoteAccountClose,
}

pub trait PrintProgramError {
Expand Down Expand Up @@ -90,6 +92,7 @@ impl PrintProgramError for ProgramError {
Self::UnsupportedSysvar => msg!("Error: UnsupportedSysvar"),
Self::IllegalOwner => msg!("Error: IllegalOwner"),
Self::AccountsDataBudgetExceeded => msg!("Error: AccountsDataBudgetExceeded"),
Self::ActiveVoteAccountClose => msg!("Error: ActiveVoteAccountClose"),
}
}
}
Expand Down Expand Up @@ -121,6 +124,7 @@ pub const ACCOUNT_NOT_RENT_EXEMPT: u64 = to_builtin!(16);
pub const UNSUPPORTED_SYSVAR: u64 = to_builtin!(17);
pub const ILLEGAL_OWNER: u64 = to_builtin!(18);
pub const ACCOUNTS_DATA_BUDGET_EXCEEDED: u64 = to_builtin!(19);
pub const ACTIVE_VOTE_ACCOUNT_CLOSE: u64 = to_builtin!(20);
// Warning: Any new program errors added here must also be:
// - Added to the below conversions
// - Added as an equivilent to InstructionError
Expand Down Expand Up @@ -148,6 +152,7 @@ impl From<ProgramError> for u64 {
ProgramError::UnsupportedSysvar => UNSUPPORTED_SYSVAR,
ProgramError::IllegalOwner => ILLEGAL_OWNER,
ProgramError::AccountsDataBudgetExceeded => ACCOUNTS_DATA_BUDGET_EXCEEDED,
ProgramError::ActiveVoteAccountClose => ACTIVE_VOTE_ACCOUNT_CLOSE,
ProgramError::Custom(error) => {
if error == 0 {
CUSTOM_ZERO
Expand Down Expand Up @@ -181,6 +186,7 @@ impl From<u64> for ProgramError {
UNSUPPORTED_SYSVAR => Self::UnsupportedSysvar,
ILLEGAL_OWNER => Self::IllegalOwner,
ACCOUNTS_DATA_BUDGET_EXCEEDED => Self::AccountsDataBudgetExceeded,
ACTIVE_VOTE_ACCOUNT_CLOSE => Self::ActiveVoteAccountClose,
_ => Self::Custom(error as u32),
}
}
Expand Down Expand Up @@ -210,6 +216,7 @@ impl TryFrom<InstructionError> for ProgramError {
Self::Error::UnsupportedSysvar => Ok(Self::UnsupportedSysvar),
Self::Error::IllegalOwner => Ok(Self::IllegalOwner),
Self::Error::AccountsDataBudgetExceeded => Ok(Self::AccountsDataBudgetExceeded),
Self::Error::ActiveVoteAccountClose => Ok(Self::ActiveVoteAccountClose),
_ => Err(error),
}
}
Expand Down Expand Up @@ -241,6 +248,7 @@ where
UNSUPPORTED_SYSVAR => Self::UnsupportedSysvar,
ILLEGAL_OWNER => Self::IllegalOwner,
ACCOUNTS_DATA_BUDGET_EXCEEDED => Self::AccountsDataBudgetExceeded,
ACTIVE_VOTE_ACCOUNT_CLOSE => Self::ActiveVoteAccountClose,
_ => {
// A valid custom error has no bits set in the upper 32
if error >> BUILTIN_BIT_SHIFT == 0 {
Expand Down
5 changes: 5 additions & 0 deletions sdk/src/feature_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,10 @@ pub mod spl_associated_token_account_v1_0_4 {
solana_sdk::declare_id!("FaTa4SpiaSNH44PGC4z8bnGVTkSRYaWvrBs3KTu8XQQq");
}

pub mod reject_vote_account_close_unless_zero_credit_epoch {
solana_sdk::declare_id!("ALBk3EWdeAg2WAGf6GPDUf1nynyNqCdEVmgouG7rpuCj");
}

lazy_static! {
/// Map of feature identifiers to user-visible description
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
Expand Down Expand Up @@ -378,6 +382,7 @@ lazy_static! {
(update_syscall_base_costs::id(), "Update syscall base costs"),
(vote_withdraw_authority_may_change_authorized_voter::id(), "vote account withdraw authority may change the authorized voter #22521"),
(spl_associated_token_account_v1_0_4::id(), "SPL Associated Token Account Program release version 1.0.4, tied to token 3.3.0 #22648"),
(reject_vote_account_close_unless_zero_credit_epoch::id(), "fail vote account withdraw to 0 unless account earned 0 credits in last completed epoch"),
/*************** ADD NEW FEATURES HERE ***************/
]
.iter()
Expand Down
1 change: 1 addition & 0 deletions storage-proto/proto/transaction_by_addr.proto
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ enum InstructionErrorType {
UNSUPPORTED_SYSVAR = 48;
ILLEGAL_OWNER = 49;
ACCOUNTS_DATA_BUDGET_EXCEEDED = 50;
ACTIVE_VOTE_ACCOUNT_CLOSE = 51;
}

message UnixTimestamp {
Expand Down
4 changes: 4 additions & 0 deletions storage-proto/src/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,7 @@ impl TryFrom<tx_by_addr::TransactionError> for TransactionError {
48 => InstructionError::UnsupportedSysvar,
49 => InstructionError::IllegalOwner,
50 => InstructionError::AccountsDataBudgetExceeded,
51 => InstructionError::ActiveVoteAccountClose,
_ => return Err("Invalid InstructionError"),
};

Expand Down Expand Up @@ -979,6 +980,9 @@ impl From<TransactionError> for tx_by_addr::TransactionError {
InstructionError::AccountsDataBudgetExceeded => {
tx_by_addr::InstructionErrorType::AccountsDataBudgetExceeded
}
InstructionError::ActiveVoteAccountClose => {
tx_by_addr::InstructionErrorType::ActiveVoteAccountClose
}
} as i32,
custom: match instruction_error {
InstructionError::Custom(custom) => {
Expand Down

0 comments on commit 9403e2f

Please sign in to comment.