1use {
2 crate::{bank::Bank, prioritization_fee::PrioritizationFee},
3 crossbeam_channel::{unbounded, Receiver, Sender, TryRecvError},
4 log::*,
5 solana_accounts_db::account_locks::validate_account_locks,
6 solana_clock::{BankId, Slot},
7 solana_measure::measure_us,
8 solana_pubkey::Pubkey,
9 solana_runtime_transaction::transaction_with_meta::TransactionWithMeta,
10 std::{
11 collections::{BTreeMap, HashMap},
12 sync::{
13 atomic::{AtomicU64, Ordering},
14 Arc, RwLock,
15 },
16 thread::{sleep, Builder, JoinHandle},
17 time::Duration,
18 },
19};
20
21const MAX_NUM_RECENT_BLOCKS: u64 = 150;
25
26const MAX_UNFINALIZED_SLOTS: u64 = 128;
28
29type UnfinalizedPrioritizationFees = BTreeMap<Slot, HashMap<BankId, PrioritizationFee>>;
30
31#[derive(Debug, Default)]
32struct PrioritizationFeeCacheMetrics {
33 successful_transaction_update_count: AtomicU64,
35
36 purged_duplicated_bank_count: AtomicU64,
38
39 total_update_elapsed_us: AtomicU64,
41
42 total_cache_lock_elapsed_us: AtomicU64,
44
45 total_entry_update_elapsed_us: AtomicU64,
47
48 total_block_finalize_elapsed_us: AtomicU64,
50
51 total_calculate_prioritization_fee_elapsed_us: AtomicU64,
53}
54
55impl PrioritizationFeeCacheMetrics {
56 fn accumulate_successful_transaction_update_count(&self, val: u64) {
57 self.successful_transaction_update_count
58 .fetch_add(val, Ordering::Relaxed);
59 }
60
61 fn accumulate_total_purged_duplicated_bank_count(&self, val: u64) {
62 self.purged_duplicated_bank_count
63 .fetch_add(val, Ordering::Relaxed);
64 }
65
66 fn accumulate_total_update_elapsed_us(&self, val: u64) {
67 self.total_update_elapsed_us
68 .fetch_add(val, Ordering::Relaxed);
69 }
70
71 fn accumulate_total_cache_lock_elapsed_us(&self, val: u64) {
72 self.total_cache_lock_elapsed_us
73 .fetch_add(val, Ordering::Relaxed);
74 }
75
76 fn accumulate_total_entry_update_elapsed_us(&self, val: u64) {
77 self.total_entry_update_elapsed_us
78 .fetch_add(val, Ordering::Relaxed);
79 }
80
81 fn accumulate_total_block_finalize_elapsed_us(&self, val: u64) {
82 self.total_block_finalize_elapsed_us
83 .fetch_add(val, Ordering::Relaxed);
84 }
85
86 fn accumulate_total_calculate_prioritization_fee_elapsed_us(&self, val: u64) {
87 self.total_calculate_prioritization_fee_elapsed_us
88 .fetch_add(val, Ordering::Relaxed);
89 }
90
91 fn report(&self, slot: Slot) {
92 datapoint_info!(
93 "block_prioritization_fee_counters",
94 ("slot", slot as i64, i64),
95 (
96 "successful_transaction_update_count",
97 self.successful_transaction_update_count
98 .swap(0, Ordering::Relaxed) as i64,
99 i64
100 ),
101 (
102 "purged_duplicated_bank_count",
103 self.purged_duplicated_bank_count.swap(0, Ordering::Relaxed) as i64,
104 i64
105 ),
106 (
107 "total_update_elapsed_us",
108 self.total_update_elapsed_us.swap(0, Ordering::Relaxed) as i64,
109 i64
110 ),
111 (
112 "total_cache_lock_elapsed_us",
113 self.total_cache_lock_elapsed_us.swap(0, Ordering::Relaxed) as i64,
114 i64
115 ),
116 (
117 "total_entry_update_elapsed_us",
118 self.total_entry_update_elapsed_us
119 .swap(0, Ordering::Relaxed) as i64,
120 i64
121 ),
122 (
123 "total_block_finalize_elapsed_us",
124 self.total_block_finalize_elapsed_us
125 .swap(0, Ordering::Relaxed) as i64,
126 i64
127 ),
128 (
129 "total_calculate_prioritization_fee_elapsed_us",
130 self.total_calculate_prioritization_fee_elapsed_us
131 .swap(0, Ordering::Relaxed) as i64,
132 i64
133 ),
134 );
135 }
136}
137
138#[derive(Debug)]
139enum CacheServiceUpdate {
140 TransactionUpdate {
141 slot: Slot,
142 bank_id: BankId,
143 compute_unit_price: u64,
144 prioritization_fee: u64,
145 writable_accounts: Vec<Pubkey>,
146 },
147 BankFinalized {
148 slot: Slot,
149 bank_id: BankId,
150 },
151 Exit,
152}
153
154#[derive(Debug)]
158pub struct PrioritizationFeeCache {
159 cache: Arc<RwLock<BTreeMap<Slot, PrioritizationFee>>>,
160 service_thread: Option<JoinHandle<()>>,
161 sender: Sender<CacheServiceUpdate>,
162 metrics: Arc<PrioritizationFeeCacheMetrics>,
163}
164
165impl Default for PrioritizationFeeCache {
166 fn default() -> Self {
167 Self::new(MAX_NUM_RECENT_BLOCKS)
168 }
169}
170
171impl Drop for PrioritizationFeeCache {
172 fn drop(&mut self) {
173 let _ = self.sender.send(CacheServiceUpdate::Exit);
174 self.service_thread
175 .take()
176 .unwrap()
177 .join()
178 .expect("Prioritization fee cache servicing thread failed to join");
179 }
180}
181
182impl PrioritizationFeeCache {
183 pub fn new(capacity: u64) -> Self {
184 let cache = Arc::new(RwLock::new(BTreeMap::new()));
185 let (sender, receiver) = unbounded();
186 let metrics = Arc::new(PrioritizationFeeCacheMetrics::default());
187
188 let service_thread = Some(
189 Builder::new()
190 .name("solPrFeeCachSvc".to_string())
191 .spawn({
192 let cache = cache.clone();
193 let metrics = metrics.clone();
194 move || Self::service_loop(cache, capacity as usize, receiver, metrics)
195 })
196 .unwrap(),
197 );
198
199 PrioritizationFeeCache {
200 cache,
201 service_thread,
202 sender,
203 metrics,
204 }
205 }
206
207 pub fn update<'a, Tx: TransactionWithMeta + 'a>(
211 &self,
212 bank: &Bank,
213 txs: impl Iterator<Item = &'a Tx>,
214 ) {
215 let (_, send_updates_us) = measure_us!({
216 for sanitized_transaction in txs {
217 if sanitized_transaction.is_simple_vote_transaction() {
220 continue;
221 }
222
223 let compute_budget_limits = sanitized_transaction
224 .compute_budget_instruction_details()
225 .sanitize_and_convert_to_compute_budget_limits(&bank.feature_set);
226
227 let lock_result = validate_account_locks(
228 sanitized_transaction.account_keys(),
229 bank.get_transaction_account_lock_limit(),
230 );
231
232 if compute_budget_limits.is_err() || lock_result.is_err() {
233 continue;
234 }
235 let compute_budget_limits = compute_budget_limits.unwrap();
236
237 if compute_budget_limits.compute_unit_limit == 0 {
240 continue;
241 }
242
243 let writable_accounts = sanitized_transaction
244 .account_keys()
245 .iter()
246 .enumerate()
247 .filter(|(index, _)| sanitized_transaction.is_writable(*index))
248 .map(|(_, key)| *key)
249 .collect();
250
251 let (prioritization_fee, calculate_prioritization_fee_us) = measure_us!({
252 solana_fee_structure::FeeBudgetLimits::from(compute_budget_limits)
253 .prioritization_fee
254 });
255 self.metrics
256 .accumulate_total_calculate_prioritization_fee_elapsed_us(
257 calculate_prioritization_fee_us,
258 );
259
260 self.sender
261 .send(CacheServiceUpdate::TransactionUpdate {
262 slot: bank.slot(),
263 bank_id: bank.bank_id(),
264 compute_unit_price: compute_budget_limits.compute_unit_price,
265 prioritization_fee,
266 writable_accounts,
267 })
268 .unwrap_or_else(|err| {
269 warn!("prioritization fee cache transaction updates failed: {err:?}");
270 });
271 }
272 });
273
274 self.metrics
275 .accumulate_total_update_elapsed_us(send_updates_us);
276 }
277
278 pub fn finalize_priority_fee(&self, slot: Slot, bank_id: BankId) {
281 self.sender
282 .send(CacheServiceUpdate::BankFinalized { slot, bank_id })
283 .unwrap_or_else(|err| {
284 warn!("prioritization fee cache signalling bank frozen failed: {err:?}")
285 });
286 }
287
288 fn update_cache(
290 unfinalized: &mut UnfinalizedPrioritizationFees,
291 slot: Slot,
292 bank_id: BankId,
293 compute_unit_price: u64,
294 prioritization_fee: u64,
295 writable_accounts: Vec<Pubkey>,
296 metrics: &PrioritizationFeeCacheMetrics,
297 ) {
298 let (_, entry_update_us) = measure_us!(unfinalized
299 .entry(slot)
300 .or_default()
301 .entry(bank_id)
302 .or_default()
303 .update(compute_unit_price, prioritization_fee, writable_accounts));
304 metrics.accumulate_total_entry_update_elapsed_us(entry_update_us);
305 metrics.accumulate_successful_transaction_update_count(1);
306 }
307
308 fn finalize_slot(
309 unfinalized: &mut UnfinalizedPrioritizationFees,
310 cache: &RwLock<BTreeMap<Slot, PrioritizationFee>>,
311 cache_max_size: usize,
312 slot: Slot,
313 bank_id: BankId,
314 metrics: &PrioritizationFeeCacheMetrics,
315 ) {
316 if unfinalized.is_empty() {
317 return;
318 }
319
320 let (slot_prioritization_fee, slot_finalize_us) = measure_us!({
324 *unfinalized =
326 unfinalized.split_off(&slot.checked_sub(MAX_UNFINALIZED_SLOTS).unwrap_or_default());
327
328 let Some(mut slot_prioritization_fee) = unfinalized.remove(&slot) else {
329 return;
330 };
331
332 let pre_purge_bank_count = slot_prioritization_fee.len() as u64;
334 let mut prioritization_fee = slot_prioritization_fee.remove(&bank_id);
335 let post_purge_bank_count = prioritization_fee.as_ref().map(|_| 1).unwrap_or(0);
336 metrics.accumulate_total_purged_duplicated_bank_count(
337 pre_purge_bank_count.saturating_sub(post_purge_bank_count),
338 );
339 if pre_purge_bank_count > 0 && post_purge_bank_count == 0 {
342 warn!(
343 "Finalized bank has empty prioritization fee cache. slot {slot} bank id \
344 {bank_id}"
345 );
346 }
347
348 if let Some(prioritization_fee) = &mut prioritization_fee {
349 if let Err(err) = prioritization_fee.mark_block_completed() {
350 error!("Unsuccessful finalizing slot {slot}, bank ID {bank_id}: {err:?}");
351 }
352 prioritization_fee.report_metrics(slot);
353 }
354 prioritization_fee
355 });
356 metrics.accumulate_total_block_finalize_elapsed_us(slot_finalize_us);
357
358 if let Some(slot_prioritization_fee) = slot_prioritization_fee {
360 let (_, cache_lock_us) = measure_us!({
361 let mut cache = cache.write().unwrap();
362 while cache.len() >= cache_max_size {
363 cache.pop_first();
364 }
365 cache.insert(slot, slot_prioritization_fee);
366 });
367 metrics.accumulate_total_cache_lock_elapsed_us(cache_lock_us);
368 }
369 }
370
371 fn service_loop(
372 cache: Arc<RwLock<BTreeMap<Slot, PrioritizationFee>>>,
373 cache_max_size: usize,
374 receiver: Receiver<CacheServiceUpdate>,
375 metrics: Arc<PrioritizationFeeCacheMetrics>,
376 ) {
377 let mut unfinalized = UnfinalizedPrioritizationFees::new();
380
381 loop {
382 let update = match receiver.try_recv() {
383 Ok(update) => update,
384 Err(TryRecvError::Empty) => {
385 sleep(Duration::from_millis(5));
386 continue;
387 }
388 Err(err @ TryRecvError::Disconnected) => {
389 info!("PrioritizationFeeCache::service_loop() is stopping because: {err}");
390 break;
391 }
392 };
393 match update {
394 CacheServiceUpdate::TransactionUpdate {
395 slot,
396 bank_id,
397 compute_unit_price,
398 prioritization_fee,
399 writable_accounts,
400 } => Self::update_cache(
401 &mut unfinalized,
402 slot,
403 bank_id,
404 compute_unit_price,
405 prioritization_fee,
406 writable_accounts,
407 &metrics,
408 ),
409 CacheServiceUpdate::BankFinalized { slot, bank_id } => {
410 Self::finalize_slot(
411 &mut unfinalized,
412 &cache,
413 cache_max_size,
414 slot,
415 bank_id,
416 &metrics,
417 );
418 metrics.report(slot);
419 }
420 CacheServiceUpdate::Exit => {
421 break;
422 }
423 }
424 }
425 }
426
427 pub fn available_block_count(&self) -> usize {
429 self.cache.read().unwrap().len()
430 }
431
432 pub fn get_prioritization_fees(&self, account_keys: &[Pubkey]) -> Vec<(Slot, u64)> {
433 self.cache
434 .read()
435 .unwrap()
436 .iter()
437 .map(|(slot, slot_prioritization_fee)| {
438 let mut fee = slot_prioritization_fee
439 .get_min_compute_unit_price()
440 .unwrap_or_default();
441 for account_key in account_keys {
442 if let Some(account_fee) =
443 slot_prioritization_fee.get_writable_account_fee(account_key)
444 {
445 fee = std::cmp::max(fee, account_fee);
446 }
447 }
448 (*slot, fee)
449 })
450 .collect()
451 }
452}
453
454#[cfg(test)]
455mod tests {
456 use {
457 super::*,
458 crate::{
459 bank::Bank,
460 bank_forks::BankForks,
461 genesis_utils::{create_genesis_config, GenesisConfigInfo},
462 },
463 solana_compute_budget_interface::ComputeBudgetInstruction,
464 solana_message::Message,
465 solana_pubkey::Pubkey,
466 solana_runtime_transaction::runtime_transaction::RuntimeTransaction,
467 solana_system_interface::instruction as system_instruction,
468 solana_transaction::{sanitized::SanitizedTransaction, Transaction},
469 };
470
471 fn build_sanitized_transaction_for_test(
472 compute_unit_price: u64,
473 signer_account: &Pubkey,
474 write_account: &Pubkey,
475 ) -> RuntimeTransaction<SanitizedTransaction> {
476 let transaction = Transaction::new_unsigned(Message::new(
477 &[
478 system_instruction::transfer(signer_account, write_account, 1),
479 ComputeBudgetInstruction::set_compute_unit_price(compute_unit_price),
480 ],
481 Some(signer_account),
482 ));
483
484 RuntimeTransaction::from_transaction_for_tests(transaction)
485 }
486
487 fn sync_update<'a>(
489 prioritization_fee_cache: &PrioritizationFeeCache,
490 bank: Arc<Bank>,
491 txs: impl ExactSizeIterator<Item = &'a RuntimeTransaction<SanitizedTransaction>>,
492 ) {
493 let expected_update_count = prioritization_fee_cache
494 .metrics
495 .successful_transaction_update_count
496 .load(Ordering::Relaxed)
497 .saturating_add(txs.len() as u64);
498
499 prioritization_fee_cache.update(&bank, txs);
500
501 while prioritization_fee_cache
503 .metrics
504 .successful_transaction_update_count
505 .load(Ordering::Relaxed)
506 != expected_update_count
507 {
508 std::thread::sleep(std::time::Duration::from_millis(10));
509 }
510 }
511
512 fn sync_finalize_priority_fee_for_test(
514 prioritization_fee_cache: &PrioritizationFeeCache,
515 slot: Slot,
516 bank_id: BankId,
517 ) {
518 prioritization_fee_cache.finalize_priority_fee(slot, bank_id);
520
521 loop {
523 let cache = prioritization_fee_cache.cache.read().unwrap();
524 if let Some(slot_cache) = cache.get(&slot) {
525 if slot_cache.is_finalized() {
526 return;
527 }
528 }
529 drop(cache);
530
531 std::thread::sleep(std::time::Duration::from_millis(10));
532 }
533 }
534
535 #[test]
536 fn test_prioritization_fee_cache_update() {
537 solana_logger::setup();
538 let write_account_a = Pubkey::new_unique();
539 let write_account_b = Pubkey::new_unique();
540 let write_account_c = Pubkey::new_unique();
541
542 let txs = vec![
552 build_sanitized_transaction_for_test(5, &write_account_a, &write_account_b),
553 build_sanitized_transaction_for_test(9, &write_account_b, &write_account_c),
554 build_sanitized_transaction_for_test(2, &write_account_a, &write_account_c),
555 ];
556
557 let bank = Arc::new(Bank::default_for_tests());
558 let slot = bank.slot();
559
560 let prioritization_fee_cache = PrioritizationFeeCache::default();
561 sync_update(&prioritization_fee_cache, bank.clone(), txs.iter());
562
563 {
565 let lock = prioritization_fee_cache.cache.read().unwrap();
566 assert!(lock.get(&slot).is_none());
567 }
568
569 {
571 sync_finalize_priority_fee_for_test(&prioritization_fee_cache, slot, bank.bank_id());
572 let lock = prioritization_fee_cache.cache.read().unwrap();
573 let fee = lock.get(&slot).unwrap();
574 assert_eq!(2, fee.get_min_compute_unit_price().unwrap());
575 assert!(fee.get_writable_account_fee(&write_account_a).is_none());
576 assert_eq!(5, fee.get_writable_account_fee(&write_account_b).unwrap());
577 assert!(fee.get_writable_account_fee(&write_account_c).is_none());
578 }
579 }
580
581 #[test]
582 fn test_available_block_count() {
583 let prioritization_fee_cache = PrioritizationFeeCache::default();
584
585 let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(10_000);
586 let bank0 = Bank::new_for_benches(&genesis_config);
587 let bank_forks = BankForks::new_rw_arc(bank0);
588 let bank = bank_forks.read().unwrap().working_bank();
589 let collector = solana_pubkey::new_rand();
590
591 let bank1 = Arc::new(Bank::new_from_parent(bank.clone(), &collector, 1));
592 sync_update(
593 &prioritization_fee_cache,
594 bank1.clone(),
595 vec![build_sanitized_transaction_for_test(
596 1,
597 &Pubkey::new_unique(),
598 &Pubkey::new_unique(),
599 )]
600 .iter(),
601 );
602 sync_finalize_priority_fee_for_test(&prioritization_fee_cache, 1, bank1.bank_id());
603
604 let bank2 = Arc::new(Bank::new_from_parent(bank.clone(), &collector, 2));
606 let txs = vec![build_sanitized_transaction_for_test(
607 1,
608 &Pubkey::new_unique(),
609 &Pubkey::new_unique(),
610 )];
611 sync_update(&prioritization_fee_cache, bank2.clone(), txs.iter());
612
613 let bank3 = Arc::new(Bank::new_from_parent(bank.clone(), &collector, 3));
614 sync_update(
615 &prioritization_fee_cache,
616 bank3.clone(),
617 vec![build_sanitized_transaction_for_test(
618 1,
619 &Pubkey::new_unique(),
620 &Pubkey::new_unique(),
621 )]
622 .iter(),
623 );
624 sync_finalize_priority_fee_for_test(&prioritization_fee_cache, 3, bank3.bank_id());
625
626 assert_eq!(2, prioritization_fee_cache.available_block_count());
628 }
629
630 #[test]
631 fn test_get_prioritization_fees() {
632 solana_logger::setup();
633 let write_account_a = Pubkey::new_unique();
634 let write_account_b = Pubkey::new_unique();
635 let write_account_c = Pubkey::new_unique();
636
637 let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(10_000);
638 let bank0 = Bank::new_for_benches(&genesis_config);
639 let bank_forks = BankForks::new_rw_arc(bank0);
640 let bank = bank_forks.read().unwrap().working_bank();
641 let collector = solana_pubkey::new_rand();
642 let bank1 = Arc::new(Bank::new_from_parent(bank.clone(), &collector, 1));
643 let bank2 = Arc::new(Bank::new_from_parent(bank.clone(), &collector, 2));
644 let bank3 = Arc::new(Bank::new_from_parent(bank, &collector, 3));
645
646 let prioritization_fee_cache = PrioritizationFeeCache::default();
647
648 assert!(prioritization_fee_cache
650 .get_prioritization_fees(&[])
651 .is_empty());
652 assert!(prioritization_fee_cache
653 .get_prioritization_fees(&[write_account_a])
654 .is_empty());
655 assert!(prioritization_fee_cache
656 .get_prioritization_fees(&[write_account_b])
657 .is_empty());
658 assert!(prioritization_fee_cache
659 .get_prioritization_fees(&[write_account_c])
660 .is_empty());
661 assert!(prioritization_fee_cache
662 .get_prioritization_fees(&[write_account_a, write_account_b])
663 .is_empty());
664 assert!(prioritization_fee_cache
665 .get_prioritization_fees(&[write_account_a, write_account_b, write_account_c])
666 .is_empty());
667
668 {
670 let txs = vec![
671 build_sanitized_transaction_for_test(2, &write_account_a, &write_account_b),
672 build_sanitized_transaction_for_test(
673 1,
674 &Pubkey::new_unique(),
675 &Pubkey::new_unique(),
676 ),
677 ];
678 sync_update(&prioritization_fee_cache, bank1.clone(), txs.iter());
679 assert!(prioritization_fee_cache
681 .get_prioritization_fees(&[])
682 .is_empty());
683 assert!(prioritization_fee_cache
684 .get_prioritization_fees(&[write_account_a])
685 .is_empty());
686 assert!(prioritization_fee_cache
687 .get_prioritization_fees(&[write_account_b])
688 .is_empty());
689 assert!(prioritization_fee_cache
690 .get_prioritization_fees(&[write_account_c])
691 .is_empty());
692 assert!(prioritization_fee_cache
693 .get_prioritization_fees(&[write_account_a, write_account_b])
694 .is_empty());
695 assert!(prioritization_fee_cache
696 .get_prioritization_fees(&[write_account_a, write_account_b, write_account_c])
697 .is_empty());
698 sync_finalize_priority_fee_for_test(&prioritization_fee_cache, 1, bank1.bank_id());
700 assert_eq!(
701 vec![(1, 1)],
702 prioritization_fee_cache.get_prioritization_fees(&[])
703 );
704 assert_eq!(
705 vec![(1, 2)],
706 prioritization_fee_cache.get_prioritization_fees(&[write_account_a])
707 );
708 assert_eq!(
709 vec![(1, 2)],
710 prioritization_fee_cache.get_prioritization_fees(&[write_account_b])
711 );
712 assert_eq!(
713 vec![(1, 1)],
714 prioritization_fee_cache.get_prioritization_fees(&[write_account_c])
715 );
716 assert_eq!(
717 vec![(1, 2)],
718 prioritization_fee_cache
719 .get_prioritization_fees(&[write_account_a, write_account_b])
720 );
721 assert_eq!(
722 vec![(1, 2)],
723 prioritization_fee_cache.get_prioritization_fees(&[
724 write_account_a,
725 write_account_b,
726 write_account_c
727 ])
728 );
729 }
730
731 {
733 let txs = vec![
734 build_sanitized_transaction_for_test(4, &write_account_b, &write_account_c),
735 build_sanitized_transaction_for_test(
736 3,
737 &Pubkey::new_unique(),
738 &Pubkey::new_unique(),
739 ),
740 ];
741 sync_update(&prioritization_fee_cache, bank2.clone(), txs.iter());
742 assert_eq!(
744 vec![(1, 1)],
745 prioritization_fee_cache.get_prioritization_fees(&[])
746 );
747 assert_eq!(
748 vec![(1, 2)],
749 prioritization_fee_cache.get_prioritization_fees(&[write_account_a])
750 );
751 assert_eq!(
752 vec![(1, 2)],
753 prioritization_fee_cache.get_prioritization_fees(&[write_account_b])
754 );
755 assert_eq!(
756 vec![(1, 1)],
757 prioritization_fee_cache.get_prioritization_fees(&[write_account_c])
758 );
759 assert_eq!(
760 vec![(1, 2)],
761 prioritization_fee_cache
762 .get_prioritization_fees(&[write_account_a, write_account_b])
763 );
764 assert_eq!(
765 vec![(1, 2)],
766 prioritization_fee_cache.get_prioritization_fees(&[
767 write_account_a,
768 write_account_b,
769 write_account_c
770 ])
771 );
772 sync_finalize_priority_fee_for_test(&prioritization_fee_cache, 2, bank2.bank_id());
774 assert_eq!(
775 vec![(1, 1), (2, 3)],
776 prioritization_fee_cache.get_prioritization_fees(&[]),
777 );
778 assert_eq!(
779 vec![(1, 2), (2, 3)],
780 prioritization_fee_cache.get_prioritization_fees(&[write_account_a]),
781 );
782 assert_eq!(
783 vec![(1, 2), (2, 4)],
784 prioritization_fee_cache.get_prioritization_fees(&[write_account_b]),
785 );
786 assert_eq!(
787 vec![(1, 1), (2, 4)],
788 prioritization_fee_cache.get_prioritization_fees(&[write_account_c]),
789 );
790 assert_eq!(
791 vec![(1, 2), (2, 4)],
792 prioritization_fee_cache
793 .get_prioritization_fees(&[write_account_a, write_account_b]),
794 );
795 assert_eq!(
796 vec![(1, 2), (2, 4)],
797 prioritization_fee_cache.get_prioritization_fees(&[
798 write_account_a,
799 write_account_b,
800 write_account_c,
801 ]),
802 );
803 }
804
805 {
807 let txs = vec![
808 build_sanitized_transaction_for_test(6, &write_account_a, &write_account_c),
809 build_sanitized_transaction_for_test(
810 5,
811 &Pubkey::new_unique(),
812 &Pubkey::new_unique(),
813 ),
814 ];
815 sync_update(&prioritization_fee_cache, bank3.clone(), txs.iter());
816 assert_eq!(
818 vec![(1, 1), (2, 3)],
819 prioritization_fee_cache.get_prioritization_fees(&[]),
820 );
821 assert_eq!(
822 vec![(1, 2), (2, 3)],
823 prioritization_fee_cache.get_prioritization_fees(&[write_account_a]),
824 );
825 assert_eq!(
826 vec![(1, 2), (2, 4)],
827 prioritization_fee_cache.get_prioritization_fees(&[write_account_b]),
828 );
829 assert_eq!(
830 vec![(1, 1), (2, 4)],
831 prioritization_fee_cache.get_prioritization_fees(&[write_account_c]),
832 );
833 assert_eq!(
834 vec![(1, 2), (2, 4)],
835 prioritization_fee_cache
836 .get_prioritization_fees(&[write_account_a, write_account_b]),
837 );
838 assert_eq!(
839 vec![(1, 2), (2, 4)],
840 prioritization_fee_cache.get_prioritization_fees(&[
841 write_account_a,
842 write_account_b,
843 write_account_c,
844 ]),
845 );
846 sync_finalize_priority_fee_for_test(&prioritization_fee_cache, 3, bank3.bank_id());
848 assert_eq!(
849 vec![(1, 1), (2, 3), (3, 5)],
850 prioritization_fee_cache.get_prioritization_fees(&[]),
851 );
852 assert_eq!(
853 vec![(1, 2), (2, 3), (3, 6)],
854 prioritization_fee_cache.get_prioritization_fees(&[write_account_a]),
855 );
856 assert_eq!(
857 vec![(1, 2), (2, 4), (3, 5)],
858 prioritization_fee_cache.get_prioritization_fees(&[write_account_b]),
859 );
860 assert_eq!(
861 vec![(1, 1), (2, 4), (3, 6)],
862 prioritization_fee_cache.get_prioritization_fees(&[write_account_c]),
863 );
864 assert_eq!(
865 vec![(1, 2), (2, 4), (3, 6)],
866 prioritization_fee_cache
867 .get_prioritization_fees(&[write_account_a, write_account_b]),
868 );
869 assert_eq!(
870 vec![(1, 2), (2, 4), (3, 6)],
871 prioritization_fee_cache.get_prioritization_fees(&[
872 write_account_a,
873 write_account_b,
874 write_account_c,
875 ]),
876 );
877 }
878 }
879
880 #[test]
881 fn test_purge_duplicated_bank() {
882 solana_logger::setup();
885 let write_account_a = Pubkey::new_unique();
886 let write_account_b = Pubkey::new_unique();
887 let write_account_c = Pubkey::new_unique();
888
889 let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(10_000);
890 let bank0 = Bank::new_for_benches(&genesis_config);
891 let bank_forks = BankForks::new_rw_arc(bank0);
892 let bank = bank_forks.read().unwrap().working_bank();
893 let collector = solana_pubkey::new_rand();
894 let slot: Slot = 999;
895 let bank1 = Arc::new(Bank::new_from_parent(bank.clone(), &collector, slot));
896 let bank2 = Arc::new(Bank::new_from_parent(bank, &collector, slot + 1));
897
898 let prioritization_fee_cache = PrioritizationFeeCache::default();
899
900 {
902 let txs = vec![
903 build_sanitized_transaction_for_test(2, &write_account_a, &write_account_b),
904 build_sanitized_transaction_for_test(
905 1,
906 &Pubkey::new_unique(),
907 &Pubkey::new_unique(),
908 ),
909 ];
910 sync_update(&prioritization_fee_cache, bank1.clone(), txs.iter());
911 }
912
913 {
915 let txs = vec![
916 build_sanitized_transaction_for_test(4, &write_account_b, &write_account_c),
917 build_sanitized_transaction_for_test(
918 3,
919 &Pubkey::new_unique(),
920 &Pubkey::new_unique(),
921 ),
922 ];
923 sync_update(&prioritization_fee_cache, bank2.clone(), txs.iter());
924 }
925
926 {
928 sync_finalize_priority_fee_for_test(&prioritization_fee_cache, slot, bank1.bank_id());
929
930 assert_eq!(
932 vec![(slot, 1)],
933 prioritization_fee_cache.get_prioritization_fees(&[])
934 );
935 assert_eq!(
936 vec![(slot, 2)],
937 prioritization_fee_cache.get_prioritization_fees(&[write_account_a])
938 );
939 assert_eq!(
940 vec![(slot, 2)],
941 prioritization_fee_cache.get_prioritization_fees(&[write_account_b])
942 );
943 assert_eq!(
944 vec![(slot, 1)],
945 prioritization_fee_cache.get_prioritization_fees(&[write_account_c])
946 );
947 assert_eq!(
948 vec![(slot, 2)],
949 prioritization_fee_cache
950 .get_prioritization_fees(&[write_account_a, write_account_b])
951 );
952 assert_eq!(
953 vec![(slot, 2)],
954 prioritization_fee_cache.get_prioritization_fees(&[
955 write_account_a,
956 write_account_b,
957 write_account_c
958 ])
959 );
960 }
961 }
962}