diff --git a/core/src/banking_stage.rs b/core/src/banking_stage.rs index cd41a536c6..6ea21db992 100644 --- a/core/src/banking_stage.rs +++ b/core/src/banking_stage.rs @@ -1065,6 +1065,7 @@ impl BankingStage { cost_tracker: &Arc>, banking_stage_stats: &BankingStageStats, demote_program_write_locks: bool, + votes_only: bool, ) -> (Vec, Vec, Vec) { let mut retryable_transaction_packet_indexes: Vec = vec![]; @@ -1079,6 +1080,9 @@ impl BankingStage { Err(TransactionError::UnsupportedVersion) }) .ok()?; + if votes_only && !solana_runtime::bank::is_simple_vote_transaction(&tx) { + return None; + } tx.verify_precompiles(feature_set).ok()?; Some((tx, *tx_index)) }) @@ -1178,6 +1182,7 @@ impl BankingStage { cost_tracker, banking_stage_stats, bank.demote_program_write_locks(), + bank.vote_only_bank(), ); packet_conversion_time.stop(); inc_new_counter_info!("banking_stage-packet_conversion", 1); @@ -1284,6 +1289,7 @@ impl BankingStage { cost_tracker, banking_stage_stats, bank.demote_program_write_locks(), + bank.vote_only_bank(), ); unprocessed_packet_conversion_time.stop(); @@ -2963,4 +2969,58 @@ mod tests { transaction.message_data() ); } + + #[test] + fn test_transactions_from_packets() { + use solana_sdk::feature_set::FeatureSet; + use solana_vote_program::vote_state::Vote; + solana_logger::setup(); + let mut vote_packet = Packet::default(); + let vote_instruction = solana_vote_program::vote_instruction::vote( + &Pubkey::new_unique(), + &Pubkey::new_unique(), + Vote::default(), + ); + let vote_transaction = + Transaction::new_with_payer(&[vote_instruction], Some(&Pubkey::new_unique())); + Packet::populate_packet(&mut vote_packet, None, &vote_transaction).unwrap(); + let mut non_vote = Packet::default(); + let tx = system_transaction::transfer( + &Keypair::new(), + &Pubkey::new_unique(), + 2, + Hash::default(), + ); + Packet::populate_packet(&mut non_vote, None, &tx).unwrap(); + let msgs = Packets::new(vec![non_vote, vote_packet]); + let packet_indexes = [0, 1]; + let feature_set = Arc::new(FeatureSet::default()); + let cost_model = Arc::new(RwLock::new(CostModel::default())); + let cost_tracker = Arc::new(RwLock::new(CostTracker::new(cost_model))); + let banking_stage_stats = BankingStageStats::default(); + let (transactions, _transaction_to_packet_indexes, _retryable_packet_indexes) = + BankingStage::transactions_from_packets( + &msgs, + &packet_indexes, + &feature_set, + &cost_tracker, + &banking_stage_stats, + false, + true, + ); + assert_eq!(transactions.len(), 1); + assert!(!transactions[0].signatures().is_empty()); + + let (transactions, _transaction_to_packet_indexes, _retryable_packet_indexes) = + BankingStage::transactions_from_packets( + &msgs, + &packet_indexes, + &feature_set, + &cost_tracker, + &banking_stage_stats, + false, + false, + ); + assert_eq!(transactions.len(), 2); + } } diff --git a/core/src/replay_stage.rs b/core/src/replay_stage.rs index a41a4377b6..7252aa97d0 100644 --- a/core/src/replay_stage.rs +++ b/core/src/replay_stage.rs @@ -1453,12 +1453,22 @@ impl ReplayStage { poh_slot, parent_slot, root_slot ); + let root_distance = poh_slot - root_slot; + const MAX_ROOT_DISTANCE_FOR_VOTE_ONLY: Slot = 500; + let vote_only_bank = if root_distance > MAX_ROOT_DISTANCE_FOR_VOTE_ONLY { + datapoint_info!("vote-only-bank", ("slot", poh_slot, i64)); + true + } else { + false + }; + let tpu_bank = Self::new_bank_from_parent_with_notify( &parent, poh_slot, root_slot, my_pubkey, rpc_subscriptions, + vote_only_bank, ); let tpu_bank = bank_forks.write().unwrap().insert(tpu_bank); @@ -2784,6 +2794,7 @@ impl ReplayStage { forks.root(), &leader, rpc_subscriptions, + false, ); let empty: Vec = vec![]; Self::update_fork_propagated_threshold_from_votes( @@ -2810,9 +2821,10 @@ impl ReplayStage { root_slot: u64, leader: &Pubkey, rpc_subscriptions: &Arc, + vote_only_bank: bool, ) -> Bank { rpc_subscriptions.notify_slot(slot, parent.slot(), root_slot); - Bank::new_from_parent(parent, leader, slot) + Bank::new_from_parent_with_vote_only(parent, leader, slot, vote_only_bank) } fn record_rewards(bank: &Bank, rewards_recorder_sender: &Option) { diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index ebe96dce0a..1557c8f658 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -984,6 +984,8 @@ pub struct Bank { pub drop_callback: RwLock, pub freeze_started: AtomicBool, + + vote_only_bank: bool, } impl Default for BlockhashQueue { @@ -1109,6 +1111,7 @@ impl Bank { feature_set: Arc::::default(), drop_callback: RwLock::::default(), freeze_started: AtomicBool::default(), + vote_only_bank: false, } } @@ -1219,7 +1222,22 @@ impl Bank { /// Create a new bank that points to an immutable checkpoint of another bank. pub fn new_from_parent(parent: &Arc, collector_id: &Pubkey, slot: Slot) -> Self { - Self::_new_from_parent(parent, collector_id, slot, &mut null_tracer()) + Self::_new_from_parent(parent, collector_id, slot, &mut null_tracer(), false) + } + + pub fn new_from_parent_with_vote_only( + parent: &Arc, + collector_id: &Pubkey, + slot: Slot, + vote_only_bank: bool, + ) -> Self { + Self::_new_from_parent( + parent, + collector_id, + slot, + &mut null_tracer(), + vote_only_bank, + ) } pub fn new_from_parent_with_tracer( @@ -1228,7 +1246,13 @@ impl Bank { slot: Slot, reward_calc_tracer: impl FnMut(&RewardCalculationEvent), ) -> Self { - Self::_new_from_parent(parent, collector_id, slot, &mut Some(reward_calc_tracer)) + Self::_new_from_parent( + parent, + collector_id, + slot, + &mut Some(reward_calc_tracer), + false, + ) } fn _new_from_parent( @@ -1236,6 +1260,7 @@ impl Bank { collector_id: &Pubkey, slot: Slot, reward_calc_tracer: &mut Option, + vote_only_bank: bool, ) -> Self { parent.freeze(); assert_ne!(slot, parent.slot()); @@ -1284,6 +1309,7 @@ impl Bank { fee_calculator: fee_rate_governor.create_fee_calculator(), fee_rate_governor, capitalization: AtomicU64::new(parent.capitalization()), + vote_only_bank, inflation: parent.inflation.clone(), transaction_count: AtomicU64::new(parent.transaction_count()), transaction_error_count: AtomicU64::new(0), @@ -1374,6 +1400,10 @@ impl Bank { *self.drop_callback.write().unwrap() = OptionalDropCallback(callback); } + pub fn vote_only_bank(&self) -> bool { + self.vote_only_bank + } + /// Like `new_from_parent` but additionally: /// * Doesn't assume that the parent is anywhere near `slot`, parent could be millions of slots /// in the past @@ -1469,6 +1499,7 @@ impl Bank { feature_set: new(), drop_callback: RwLock::new(OptionalDropCallback(None)), freeze_started: AtomicBool::new(fields.hash != Hash::default()), + vote_only_bank: false, }; bank.finish_init( genesis_config, @@ -5698,7 +5729,7 @@ pub fn goto_end_of_slot(bank: &mut Bank) { } } -fn is_simple_vote_transaction(transaction: &SanitizedTransaction) -> bool { +pub fn is_simple_vote_transaction(transaction: &SanitizedTransaction) -> bool { if transaction.message().instructions().len() == 1 { let (program_pubkey, instruction) = transaction .message()