#![allow(clippy::implicit_hasher)] use { crate::{sigverify, sigverify_stage::SigVerifier}, solana_ledger::{ leader_schedule_cache::LeaderScheduleCache, shred::Shred, sigverify_shreds::verify_shreds_gpu, }, solana_perf::{self, packet::PacketBatch, recycler_cache::RecyclerCache}, solana_runtime::bank_forks::BankForks, std::{ collections::{HashMap, HashSet}, sync::{Arc, RwLock}, }, }; #[derive(Clone)] pub struct ShredSigVerifier { bank_forks: Arc>, leader_schedule_cache: Arc, recycler_cache: RecyclerCache, } impl ShredSigVerifier { pub fn new( bank_forks: Arc>, leader_schedule_cache: Arc, ) -> Self { sigverify::init(); Self { bank_forks, leader_schedule_cache, recycler_cache: RecyclerCache::warmed(), } } fn read_slots(batches: &[PacketBatch]) -> HashSet { batches .iter() .flat_map(|batch| batch.iter().filter_map(Shred::get_slot_from_packet)) .collect() } } impl SigVerifier for ShredSigVerifier { fn verify_batches( &self, mut batches: Vec, _valid_packets: usize, ) -> Vec { let r_bank = self.bank_forks.read().unwrap().working_bank(); let slots: HashSet = Self::read_slots(&batches); let mut leader_slots: HashMap = slots .into_iter() .filter_map(|slot| { let key = self .leader_schedule_cache .slot_leader_at(slot, Some(&r_bank))?; Some((slot, key.to_bytes())) }) .collect(); leader_slots.insert(std::u64::MAX, [0u8; 32]); let r = verify_shreds_gpu(&batches, &leader_slots, &self.recycler_cache); solana_perf::sigverify::mark_disabled(&mut batches, &r); batches } } #[cfg(test)] pub mod tests { use { super::*, solana_ledger::{ genesis_utils::create_genesis_config_with_leader, shred::{Shred, ShredFlags}, }, solana_perf::packet::Packet, solana_runtime::bank::Bank, solana_sdk::signature::{Keypair, Signer}, }; #[test] fn test_sigverify_shreds_read_slots() { solana_logger::setup(); let mut shred = Shred::new_from_data( 0xdead_c0de, 0xc0de, 0xdead, &[1, 2, 3, 4], ShredFlags::LAST_SHRED_IN_SLOT, 0, 0, 0xc0de, ); let mut batches: Vec<_> = (0..2) .map(|_| { let mut batch = PacketBatch::with_capacity(1); batch.resize(1, Packet::default()); batch }) .collect(); let keypair = Keypair::new(); shred.sign(&keypair); batches[0][0].data[0..shred.payload().len()].copy_from_slice(shred.payload()); batches[0][0].meta.size = shred.payload().len(); let mut shred = Shred::new_from_data( 0xc0de_dead, 0xc0de, 0xdead, &[1, 2, 3, 4], ShredFlags::LAST_SHRED_IN_SLOT, 0, 0, 0xc0de, ); shred.sign(&keypair); batches[1][0].data[0..shred.payload().len()].copy_from_slice(shred.payload()); batches[1][0].meta.size = shred.payload().len(); let expected: HashSet = [0xc0de_dead, 0xdead_c0de].iter().cloned().collect(); assert_eq!(ShredSigVerifier::read_slots(&batches), expected); } #[test] fn test_sigverify_shreds_verify_batches() { let leader_keypair = Arc::new(Keypair::new()); let leader_pubkey = leader_keypair.pubkey(); let bank = Bank::new_for_tests( &create_genesis_config_with_leader(100, &leader_pubkey, 10).genesis_config, ); let cache = Arc::new(LeaderScheduleCache::new_from_bank(&bank)); let bf = Arc::new(RwLock::new(BankForks::new(bank))); let verifier = ShredSigVerifier::new(bf, cache); let batch_size = 2; let mut batch = PacketBatch::with_capacity(batch_size); batch.resize(batch_size, Packet::default()); let mut batches = vec![batch]; let mut shred = Shred::new_from_data( 0, 0xc0de, 0xdead, &[1, 2, 3, 4], ShredFlags::LAST_SHRED_IN_SLOT, 0, 0, 0xc0de, ); shred.sign(&leader_keypair); batches[0][0].data[0..shred.payload().len()].copy_from_slice(shred.payload()); batches[0][0].meta.size = shred.payload().len(); let mut shred = Shred::new_from_data( 0, 0xbeef, 0xc0de, &[1, 2, 3, 4], ShredFlags::LAST_SHRED_IN_SLOT, 0, 0, 0xc0de, ); let wrong_keypair = Keypair::new(); shred.sign(&wrong_keypair); batches[0][1].data[0..shred.payload().len()].copy_from_slice(shred.payload()); batches[0][1].meta.size = shred.payload().len(); let num_packets = solana_perf::sigverify::count_packets_in_batches(&batches); let rv = verifier.verify_batches(batches, num_packets); assert!(!rv[0][0].meta.discard()); assert!(rv[0][1].meta.discard()); } }