Add Bank support for "upgrade epochs" where all non-vote transactions will be rejected
This commit is contained in:
parent
fdff681bcc
commit
e5d8c4383f
|
@ -74,6 +74,7 @@ pub struct ErrorCounters {
|
|||
pub invalid_account_for_fee: usize,
|
||||
pub invalid_account_index: usize,
|
||||
pub invalid_program_for_execution: usize,
|
||||
pub not_allowed_during_cluster_maintenance: usize,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, PartialEq, Clone)]
|
||||
|
|
|
@ -45,6 +45,7 @@ use solana_sdk::{
|
|||
incinerator,
|
||||
inflation::Inflation,
|
||||
native_loader, nonce,
|
||||
program_utils::limited_deserialize,
|
||||
pubkey::Pubkey,
|
||||
signature::{Keypair, Signature},
|
||||
slot_hashes::SlotHashes,
|
||||
|
@ -55,7 +56,7 @@ use solana_sdk::{
|
|||
transaction::{Result, Transaction, TransactionError},
|
||||
};
|
||||
use solana_stake_program::stake_state::{self, Delegation};
|
||||
use solana_vote_program::vote_state::VoteState;
|
||||
use solana_vote_program::{vote_instruction::VoteInstruction, vote_state::VoteState};
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
collections::{HashMap, HashSet},
|
||||
|
@ -73,7 +74,7 @@ pub const SECONDS_PER_YEAR: f64 = 365.25 * 24.0 * 60.0 * 60.0;
|
|||
pub const MAX_LEADER_SCHEDULE_STAKES: Epoch = 5;
|
||||
|
||||
type BankStatusCache = StatusCache<Result<()>>;
|
||||
#[frozen_abi(digest = "9chBcbXVJ4fK7uGgydQzam5aHipaAKFw6V4LDFpjbE4w")]
|
||||
#[frozen_abi(digest = "BHtoJzwGJ1seQ2gZmtPSLLgdvq3gRZMj5mpUJsX4wGHT")]
|
||||
pub type BankSlotDelta = SlotDelta<Result<()>>;
|
||||
type TransactionAccountRefCells = Vec<Rc<RefCell<Account>>>;
|
||||
type TransactionLoaderRefCells = Vec<Vec<(Pubkey, RefCell<Account>)>>;
|
||||
|
@ -1404,6 +1405,43 @@ impl Bank {
|
|||
})
|
||||
.collect()
|
||||
}
|
||||
fn filter_by_vote_transactions(
|
||||
&self,
|
||||
txs: &[Transaction],
|
||||
iteration_order: Option<&[usize]>,
|
||||
lock_results: Vec<TransactionProcessResult>,
|
||||
error_counters: &mut ErrorCounters,
|
||||
) -> Vec<TransactionProcessResult> {
|
||||
OrderedIterator::new(txs, iteration_order)
|
||||
.zip(lock_results.into_iter())
|
||||
.map(|(tx, lock_res)| {
|
||||
if lock_res.0.is_ok() {
|
||||
if tx.message.instructions.len() == 1 {
|
||||
let instruction = &tx.message.instructions[0];
|
||||
let program_pubkey =
|
||||
tx.message.account_keys[instruction.program_id_index as usize];
|
||||
if program_pubkey == solana_vote_program::id() {
|
||||
if let Ok(vote_instruction) =
|
||||
limited_deserialize::<VoteInstruction>(&instruction.data)
|
||||
{
|
||||
match vote_instruction {
|
||||
VoteInstruction::Vote(_)
|
||||
| VoteInstruction::VoteSwitch(_, _) => {
|
||||
return lock_res;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
error_counters.not_allowed_during_cluster_maintenance += 1;
|
||||
return (Err(TransactionError::ClusterMaintenance), lock_res.1);
|
||||
}
|
||||
lock_res
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn check_hash_age(&self, hash: &Hash, max_age: usize) -> Option<bool> {
|
||||
self.blockhash_queue
|
||||
|
@ -1424,6 +1462,18 @@ impl Bank {
|
|||
})
|
||||
}
|
||||
|
||||
// Determine if the bank is currently in an upgrade epoch, where only votes are permitted
|
||||
fn upgrade_epoch(&self) -> bool {
|
||||
match self.operating_mode() {
|
||||
#[cfg(test)]
|
||||
OperatingMode::Development => self.epoch == 0xdead, // Value assumed by `test_upgrade_epoch()`
|
||||
#[cfg(not(test))]
|
||||
OperatingMode::Development => false,
|
||||
OperatingMode::Preview => false,
|
||||
OperatingMode::Stable => self.epoch == Epoch::max_value(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_transactions(
|
||||
&self,
|
||||
txs: &[Transaction],
|
||||
|
@ -1439,7 +1489,19 @@ impl Bank {
|
|||
max_age,
|
||||
&mut error_counters,
|
||||
);
|
||||
self.check_signatures(txs, iteration_order, age_results, &mut error_counters)
|
||||
let sigcheck_results =
|
||||
self.check_signatures(txs, iteration_order, age_results, &mut error_counters);
|
||||
if self.upgrade_epoch() {
|
||||
// Reject all non-vote transactions
|
||||
self.filter_by_vote_transactions(
|
||||
txs,
|
||||
iteration_order,
|
||||
sigcheck_results,
|
||||
&mut error_counters,
|
||||
)
|
||||
} else {
|
||||
sigcheck_results
|
||||
}
|
||||
}
|
||||
|
||||
pub fn collect_balances(&self, batch: &[Transaction]) -> TransactionBalances {
|
||||
|
@ -1522,6 +1584,12 @@ impl Bank {
|
|||
error_counters.duplicate_signature
|
||||
);
|
||||
}
|
||||
if 0 != error_counters.not_allowed_during_cluster_maintenance {
|
||||
inc_new_counter_error!(
|
||||
"bank-process_transactions-error-cluster-maintenance",
|
||||
error_counters.not_allowed_during_cluster_maintenance
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts Accounts into RefCell<Account>, this involves moving
|
||||
|
@ -7728,4 +7796,96 @@ mod tests {
|
|||
consumed_budgets.sort();
|
||||
assert_eq!(consumed_budgets, vec![0, 1, 8]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_upgrade_epoch() {
|
||||
let GenesisConfigInfo {
|
||||
mut genesis_config,
|
||||
mint_keypair,
|
||||
..
|
||||
} = create_genesis_config_with_leader(500, &Pubkey::new_rand(), 0);
|
||||
genesis_config.fee_rate_governor = FeeRateGovernor::new(1, 0);
|
||||
let bank = Arc::new(Bank::new(&genesis_config));
|
||||
|
||||
// Jump to the test-only upgrade epoch -- see `Bank::upgrade_epoch()`
|
||||
let bank = Bank::new_from_parent(
|
||||
&bank,
|
||||
&Pubkey::default(),
|
||||
genesis_config
|
||||
.epoch_schedule
|
||||
.get_first_slot_in_epoch(0xdead),
|
||||
);
|
||||
|
||||
assert_eq!(bank.get_balance(&mint_keypair.pubkey()), 500);
|
||||
|
||||
// Normal transfers are not allowed
|
||||
assert_eq!(
|
||||
bank.transfer(2, &mint_keypair, &mint_keypair.pubkey()),
|
||||
Err(TransactionError::ClusterMaintenance)
|
||||
);
|
||||
assert_eq!(bank.get_balance(&mint_keypair.pubkey()), 500); // no transaction fee charged
|
||||
|
||||
let vote_pubkey = Pubkey::new_rand();
|
||||
let authorized_voter = Keypair::new();
|
||||
|
||||
// VoteInstruction::Vote is allowed. The transaction fails with a vote program instruction
|
||||
// error because the vote account is not actually setup
|
||||
let tx = Transaction::new_signed_with_payer(
|
||||
&[vote_instruction::vote(
|
||||
&vote_pubkey,
|
||||
&authorized_voter.pubkey(),
|
||||
Vote::new(vec![1], Hash::default()),
|
||||
)],
|
||||
Some(&mint_keypair.pubkey()),
|
||||
&[&mint_keypair, &authorized_voter],
|
||||
bank.last_blockhash(),
|
||||
);
|
||||
assert_eq!(
|
||||
bank.process_transaction(&tx),
|
||||
Err(TransactionError::InstructionError(
|
||||
0,
|
||||
InstructionError::InvalidAccountData
|
||||
))
|
||||
);
|
||||
assert_eq!(bank.get_balance(&mint_keypair.pubkey()), 498); // transaction fee charged
|
||||
|
||||
// VoteInstruction::VoteSwitch is allowed. The transaction fails with a vote program
|
||||
// instruction error because the vote account is not actually setup
|
||||
let tx = Transaction::new_signed_with_payer(
|
||||
&[vote_instruction::vote_switch(
|
||||
&vote_pubkey,
|
||||
&authorized_voter.pubkey(),
|
||||
Vote::new(vec![1], Hash::default()),
|
||||
Hash::default(),
|
||||
)],
|
||||
Some(&mint_keypair.pubkey()),
|
||||
&[&mint_keypair, &authorized_voter],
|
||||
bank.last_blockhash(),
|
||||
);
|
||||
assert_eq!(
|
||||
bank.process_transaction(&tx),
|
||||
Err(TransactionError::InstructionError(
|
||||
0,
|
||||
InstructionError::InvalidAccountData
|
||||
))
|
||||
);
|
||||
assert_eq!(bank.get_balance(&mint_keypair.pubkey()), 496); // transaction fee charged
|
||||
|
||||
// Other vote program instructions, like VoteInstruction::UpdateCommission are not allowed
|
||||
let tx = Transaction::new_signed_with_payer(
|
||||
&[vote_instruction::update_commission(
|
||||
&vote_pubkey,
|
||||
&authorized_voter.pubkey(),
|
||||
123,
|
||||
)],
|
||||
Some(&mint_keypair.pubkey()),
|
||||
&[&mint_keypair, &authorized_voter],
|
||||
bank.last_blockhash(),
|
||||
);
|
||||
assert_eq!(
|
||||
bank.process_transaction(&tx),
|
||||
Err(TransactionError::ClusterMaintenance)
|
||||
);
|
||||
assert_eq!(bank.get_balance(&mint_keypair.pubkey()), 496); // no transaction fee charged
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,6 +83,9 @@ pub enum TransactionError {
|
|||
/// not be unlocked.
|
||||
#[error("Transaction failed to sanitize accounts offsets correctly")]
|
||||
SanitizeFailure,
|
||||
|
||||
#[error("Transactions are currently disabled due to cluster maintenance")]
|
||||
ClusterMaintenance,
|
||||
}
|
||||
|
||||
pub type Result<T> = result::Result<T, TransactionError>;
|
||||
|
|
Loading…
Reference in New Issue