diff --git a/core/src/banking_stage.rs b/core/src/banking_stage.rs index 3fd19cf88b500c..735f960c73898a 100644 --- a/core/src/banking_stage.rs +++ b/core/src/banking_stage.rs @@ -978,12 +978,18 @@ impl BankingStage { msgs: &Packets, transaction_indexes: &[usize], libsecp256k1_0_5_upgrade_enabled: bool, + votes_only: bool, ) -> (Vec>, Vec) { transaction_indexes .iter() .filter_map(|tx_index| { let p = &msgs.packets[*tx_index]; let tx: Transaction = limited_deserialize(&p.data[0..p.meta.size]).ok()?; + + if votes_only && !solana_runtime::bank::is_simple_vote_transaction(&tx) { + return None; + } + tx.verify_precompiles(libsecp256k1_0_5_upgrade_enabled) .ok()?; let message_bytes = Self::packet_message(p)?; @@ -1050,6 +1056,7 @@ impl BankingStage { msgs, &packet_indexes, bank.libsecp256k1_0_5_upgrade_enabled(), + bank.vote_only_bank(), ); packet_conversion_time.stop(); @@ -1121,6 +1128,7 @@ impl BankingStage { msgs, &transaction_indexes, bank.libsecp256k1_0_5_upgrade_enabled(), + bank.vote_only_bank(), ); let tx_count = transaction_to_packet_indexes.len(); @@ -2786,4 +2794,37 @@ mod tests { transaction.message_data() ); } + + #[test] + fn test_transactions_from_packets() { + 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 (transactions, _transaction_to_packet_indexes) = + BankingStage::transactions_from_packets(&msgs, &packet_indexes, false, true); + assert_eq!(transactions.len(), 1); + assert!(!transactions[0].transaction().signatures.is_empty()); + + let (transactions, _transaction_to_packet_indexes) = + BankingStage::transactions_from_packets(&msgs, &packet_indexes, false, false); + assert_eq!(transactions.len(), 2); + } } diff --git a/core/src/replay_stage.rs b/core/src/replay_stage.rs index 45cd78306c3846..62d97ae3f478bf 100644 --- a/core/src/replay_stage.rs +++ b/core/src/replay_stage.rs @@ -1181,12 +1181,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, subscriptions, + vote_only_bank, ); let tpu_bank = bank_forks.write().unwrap().insert(tpu_bank); @@ -2442,6 +2452,7 @@ impl ReplayStage { forks.root(), &leader, subscriptions, + false, ); let empty: Vec = vec![]; Self::update_fork_propagated_threshold_from_votes( @@ -2468,9 +2479,10 @@ impl ReplayStage { root_slot: u64, leader: &Pubkey, subscriptions: &Arc, + vote_only_bank: bool, ) -> Bank { 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 b4a617ec22a90b..8294d6d35e14c9 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -1005,6 +1005,8 @@ pub struct Bank { pub drop_callback: RwLock, pub freeze_started: AtomicBool, + + vote_only_bank: bool, } impl Default for BlockhashQueue { @@ -1119,7 +1121,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( @@ -1128,7 +1145,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( @@ -1136,6 +1159,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()); @@ -1184,6 +1208,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), @@ -1278,6 +1303,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 @@ -1373,6 +1402,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, @@ -5412,7 +5442,7 @@ pub fn goto_end_of_slot(bank: &mut Bank) { } } -fn is_simple_vote_transaction(transaction: &Transaction) -> bool { +pub fn is_simple_vote_transaction(transaction: &Transaction) -> bool { if transaction.message.instructions.len() == 1 { let instruction = &transaction.message.instructions[0]; let program_pubkey =