1use {
2 crate::{bank::Bank, prioritization_fee::PrioritizationFee},
3 crossbeam_channel::{Receiver, Sender, TryRecvError, unbounded},
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 Arc, RwLock,
14 atomic::{AtomicU64, Ordering},
15 },
16 thread::{Builder, JoinHandle, sleep},
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 transaction_configuration =
224 sanitized_transaction.transaction_configuration(&bank.feature_set);
225 let lock_result = validate_account_locks(
226 sanitized_transaction.account_keys(),
227 bank.get_transaction_account_lock_limit(),
228 );
229
230 if transaction_configuration.is_err() || lock_result.is_err() {
231 continue;
232 }
233 let transaction_configuration = transaction_configuration.unwrap();
234
235 if transaction_configuration.compute_unit_limit == 0 {
238 continue;
239 }
240
241 let writable_accounts = sanitized_transaction
242 .account_keys()
243 .iter()
244 .enumerate()
245 .filter(|(index, _)| sanitized_transaction.is_writable(*index))
246 .map(|(_, key)| *key)
247 .collect();
248
249 let (prioritization_fee, calculate_prioritization_fee_us) =
250 measure_us!(transaction_configuration.priority_fee_lamports);
251 self.metrics
252 .accumulate_total_calculate_prioritization_fee_elapsed_us(
253 calculate_prioritization_fee_us,
254 );
255
256 let compute_unit_price =
258 transaction_configuration.compute_unit_price_in_microlamports();
259 self.sender
260 .send(CacheServiceUpdate::TransactionUpdate {
261 slot: bank.slot(),
262 bank_id: bank.bank_id(),
263 compute_unit_price,
264 prioritization_fee,
265 writable_accounts,
266 })
267 .unwrap_or_else(|err| {
268 warn!("prioritization fee cache transaction updates failed: {err:?}");
269 });
270 }
271 });
272
273 self.metrics
274 .accumulate_total_update_elapsed_us(send_updates_us);
275 }
276
277 pub fn finalize_priority_fee(&self, slot: Slot, bank_id: BankId) {
280 self.sender
281 .send(CacheServiceUpdate::BankFinalized { slot, bank_id })
282 .unwrap_or_else(|err| {
283 warn!("prioritization fee cache signalling bank frozen failed: {err:?}")
284 });
285 }
286
287 fn update_cache(
289 unfinalized: &mut UnfinalizedPrioritizationFees,
290 slot: Slot,
291 bank_id: BankId,
292 compute_unit_price: u64,
293 prioritization_fee: u64,
294 writable_accounts: Vec<Pubkey>,
295 metrics: &PrioritizationFeeCacheMetrics,
296 ) {
297 let (_, entry_update_us) = measure_us!(
298 unfinalized
299 .entry(slot)
300 .or_default()
301 .entry(bank_id)
302 .or_default()
303 .update(compute_unit_price, prioritization_fee, writable_accounts)
304 );
305 metrics.accumulate_total_entry_update_elapsed_us(entry_update_us);
306 metrics.accumulate_successful_transaction_update_count(1);
307 }
308
309 fn finalize_slot(
310 unfinalized: &mut UnfinalizedPrioritizationFees,
311 cache: &RwLock<BTreeMap<Slot, PrioritizationFee>>,
312 cache_max_size: usize,
313 slot: Slot,
314 bank_id: BankId,
315 metrics: &PrioritizationFeeCacheMetrics,
316 ) {
317 if unfinalized.is_empty() {
318 return;
319 }
320
321 let (slot_prioritization_fee, slot_finalize_us) = measure_us!({
325 *unfinalized = unfinalized.split_off(&slot.saturating_sub(MAX_UNFINALIZED_SLOTS));
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::{GenesisConfigInfo, create_genesis_config},
462 },
463 solana_compute_budget_interface::ComputeBudgetInstruction,
464 solana_leader_schedule::SlotLeader,
465 solana_message::Message,
466 solana_pubkey::Pubkey,
467 solana_runtime_transaction::runtime_transaction::RuntimeTransaction,
468 solana_system_interface::instruction as system_instruction,
469 solana_transaction::{Transaction, sanitized::SanitizedTransaction},
470 };
471
472 fn build_sanitized_transaction_for_test(
473 compute_unit_price: u64,
474 signer_account: &Pubkey,
475 write_account: &Pubkey,
476 ) -> RuntimeTransaction<SanitizedTransaction> {
477 let transaction = Transaction::new_unsigned(Message::new(
478 &[
479 system_instruction::transfer(signer_account, write_account, 1),
480 ComputeBudgetInstruction::set_compute_unit_price(compute_unit_price),
481 ],
482 Some(signer_account),
483 ));
484
485 RuntimeTransaction::from_transaction_for_tests(transaction)
486 }
487
488 fn sync_update<'a>(
490 prioritization_fee_cache: &PrioritizationFeeCache,
491 bank: Arc<Bank>,
492 txs: impl ExactSizeIterator<Item = &'a RuntimeTransaction<SanitizedTransaction>>,
493 ) {
494 let expected_update_count = prioritization_fee_cache
495 .metrics
496 .successful_transaction_update_count
497 .load(Ordering::Relaxed)
498 .saturating_add(txs.len() as u64);
499
500 prioritization_fee_cache.update(&bank, txs);
501
502 while prioritization_fee_cache
504 .metrics
505 .successful_transaction_update_count
506 .load(Ordering::Relaxed)
507 != expected_update_count
508 {
509 std::thread::sleep(std::time::Duration::from_millis(10));
510 }
511 }
512
513 fn sync_finalize_priority_fee_for_test(
515 prioritization_fee_cache: &PrioritizationFeeCache,
516 slot: Slot,
517 bank_id: BankId,
518 ) {
519 prioritization_fee_cache.finalize_priority_fee(slot, bank_id);
521
522 loop {
524 let cache = prioritization_fee_cache.cache.read().unwrap();
525 if let Some(slot_cache) = cache.get(&slot)
526 && slot_cache.is_finalized()
527 {
528 return;
529 }
530 drop(cache);
531
532 std::thread::sleep(std::time::Duration::from_millis(10));
533 }
534 }
535
536 #[test]
537 fn test_prioritization_fee_cache_update() {
538 agave_logger::setup();
539 let write_account_a = Pubkey::new_unique();
540 let write_account_b = Pubkey::new_unique();
541 let write_account_c = Pubkey::new_unique();
542
543 let txs = [
553 build_sanitized_transaction_for_test(5_000_000, &write_account_a, &write_account_b),
554 build_sanitized_transaction_for_test(9_000_000, &write_account_b, &write_account_c),
555 build_sanitized_transaction_for_test(2_000_000, &write_account_a, &write_account_c),
556 ];
557
558 let bank = Arc::new(Bank::default_for_tests());
559 let slot = bank.slot();
560
561 let prioritization_fee_cache = PrioritizationFeeCache::default();
562 sync_update(&prioritization_fee_cache, bank.clone(), txs.iter());
563
564 {
566 let lock = prioritization_fee_cache.cache.read().unwrap();
567 assert!(lock.get(&slot).is_none());
568 }
569
570 {
572 sync_finalize_priority_fee_for_test(&prioritization_fee_cache, slot, bank.bank_id());
573 let lock = prioritization_fee_cache.cache.read().unwrap();
574 let fee = lock.get(&slot).unwrap();
575 assert_eq!(2_000_000, fee.get_min_compute_unit_price().unwrap());
576 assert!(fee.get_writable_account_fee(&write_account_a).is_none());
577 assert_eq!(
578 5_000_000,
579 fee.get_writable_account_fee(&write_account_b).unwrap()
580 );
581 assert!(fee.get_writable_account_fee(&write_account_c).is_none());
582 }
583 }
584
585 #[test]
586 fn test_available_block_count() {
587 let prioritization_fee_cache = PrioritizationFeeCache::default();
588
589 let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(10_000);
590 let bank0 = Bank::new_for_benches(&genesis_config);
591 let bank_forks = BankForks::new_rw_arc(bank0);
592 let bank = bank_forks.read().unwrap().working_bank();
593 let leader = SlotLeader::new_unique();
594
595 let bank1 = Arc::new(Bank::new_from_parent(bank.clone(), leader, 1));
596 sync_update(
597 &prioritization_fee_cache,
598 bank1.clone(),
599 [build_sanitized_transaction_for_test(
600 1,
601 &Pubkey::new_unique(),
602 &Pubkey::new_unique(),
603 )]
604 .iter(),
605 );
606 sync_finalize_priority_fee_for_test(&prioritization_fee_cache, 1, bank1.bank_id());
607
608 let bank2 = Arc::new(Bank::new_from_parent(bank.clone(), leader, 2));
610 let txs = [build_sanitized_transaction_for_test(
611 1,
612 &Pubkey::new_unique(),
613 &Pubkey::new_unique(),
614 )];
615 sync_update(&prioritization_fee_cache, bank2, txs.iter());
616
617 let bank3 = Arc::new(Bank::new_from_parent(bank, leader, 3));
618 sync_update(
619 &prioritization_fee_cache,
620 bank3.clone(),
621 [build_sanitized_transaction_for_test(
622 1,
623 &Pubkey::new_unique(),
624 &Pubkey::new_unique(),
625 )]
626 .iter(),
627 );
628 sync_finalize_priority_fee_for_test(&prioritization_fee_cache, 3, bank3.bank_id());
629
630 assert_eq!(2, prioritization_fee_cache.available_block_count());
632 }
633
634 #[test]
635 fn test_get_prioritization_fees() {
636 agave_logger::setup();
637 let write_account_a = Pubkey::new_unique();
638 let write_account_b = Pubkey::new_unique();
639 let write_account_c = Pubkey::new_unique();
640
641 let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(10_000);
642 let bank0 = Bank::new_for_benches(&genesis_config);
643 let bank_forks = BankForks::new_rw_arc(bank0);
644 let bank = bank_forks.read().unwrap().working_bank();
645 let leader = SlotLeader::new_unique();
646 let bank1 = Arc::new(Bank::new_from_parent(bank.clone(), leader, 1));
647 let bank2 = Arc::new(Bank::new_from_parent(bank.clone(), leader, 2));
648 let bank3 = Arc::new(Bank::new_from_parent(bank, leader, 3));
649
650 let prioritization_fee_cache = PrioritizationFeeCache::default();
651
652 assert!(
654 prioritization_fee_cache
655 .get_prioritization_fees(&[])
656 .is_empty()
657 );
658 assert!(
659 prioritization_fee_cache
660 .get_prioritization_fees(&[write_account_a])
661 .is_empty()
662 );
663 assert!(
664 prioritization_fee_cache
665 .get_prioritization_fees(&[write_account_b])
666 .is_empty()
667 );
668 assert!(
669 prioritization_fee_cache
670 .get_prioritization_fees(&[write_account_c])
671 .is_empty()
672 );
673 assert!(
674 prioritization_fee_cache
675 .get_prioritization_fees(&[write_account_a, write_account_b])
676 .is_empty()
677 );
678 assert!(
679 prioritization_fee_cache
680 .get_prioritization_fees(&[write_account_a, write_account_b, write_account_c])
681 .is_empty()
682 );
683
684 {
686 let txs = [
687 build_sanitized_transaction_for_test(2_000_000, &write_account_a, &write_account_b),
688 build_sanitized_transaction_for_test(
689 1_000_000,
690 &Pubkey::new_unique(),
691 &Pubkey::new_unique(),
692 ),
693 ];
694 sync_update(&prioritization_fee_cache, bank1.clone(), txs.iter());
695 assert!(
697 prioritization_fee_cache
698 .get_prioritization_fees(&[])
699 .is_empty()
700 );
701 assert!(
702 prioritization_fee_cache
703 .get_prioritization_fees(&[write_account_a])
704 .is_empty()
705 );
706 assert!(
707 prioritization_fee_cache
708 .get_prioritization_fees(&[write_account_b])
709 .is_empty()
710 );
711 assert!(
712 prioritization_fee_cache
713 .get_prioritization_fees(&[write_account_c])
714 .is_empty()
715 );
716 assert!(
717 prioritization_fee_cache
718 .get_prioritization_fees(&[write_account_a, write_account_b])
719 .is_empty()
720 );
721 assert!(
722 prioritization_fee_cache
723 .get_prioritization_fees(&[write_account_a, write_account_b, write_account_c])
724 .is_empty()
725 );
726 sync_finalize_priority_fee_for_test(&prioritization_fee_cache, 1, bank1.bank_id());
728 assert_eq!(
729 vec![(1, 1_000_000)],
730 prioritization_fee_cache.get_prioritization_fees(&[])
731 );
732 assert_eq!(
733 vec![(1, 2_000_000)],
734 prioritization_fee_cache.get_prioritization_fees(&[write_account_a])
735 );
736 assert_eq!(
737 vec![(1, 2_000_000)],
738 prioritization_fee_cache.get_prioritization_fees(&[write_account_b])
739 );
740 assert_eq!(
741 vec![(1, 1_000_000)],
742 prioritization_fee_cache.get_prioritization_fees(&[write_account_c])
743 );
744 assert_eq!(
745 vec![(1, 2_000_000)],
746 prioritization_fee_cache
747 .get_prioritization_fees(&[write_account_a, write_account_b])
748 );
749 assert_eq!(
750 vec![(1, 2_000_000)],
751 prioritization_fee_cache.get_prioritization_fees(&[
752 write_account_a,
753 write_account_b,
754 write_account_c
755 ])
756 );
757 }
758
759 {
761 let txs = [
762 build_sanitized_transaction_for_test(4_000_000, &write_account_b, &write_account_c),
763 build_sanitized_transaction_for_test(
764 3_000_000,
765 &Pubkey::new_unique(),
766 &Pubkey::new_unique(),
767 ),
768 ];
769 sync_update(&prioritization_fee_cache, bank2.clone(), txs.iter());
770 assert_eq!(
772 vec![(1, 1_000_000)],
773 prioritization_fee_cache.get_prioritization_fees(&[])
774 );
775 assert_eq!(
776 vec![(1, 2_000_000)],
777 prioritization_fee_cache.get_prioritization_fees(&[write_account_a])
778 );
779 assert_eq!(
780 vec![(1, 2_000_000)],
781 prioritization_fee_cache.get_prioritization_fees(&[write_account_b])
782 );
783 assert_eq!(
784 vec![(1, 1_000_000)],
785 prioritization_fee_cache.get_prioritization_fees(&[write_account_c])
786 );
787 assert_eq!(
788 vec![(1, 2_000_000)],
789 prioritization_fee_cache
790 .get_prioritization_fees(&[write_account_a, write_account_b])
791 );
792 assert_eq!(
793 vec![(1, 2_000_000)],
794 prioritization_fee_cache.get_prioritization_fees(&[
795 write_account_a,
796 write_account_b,
797 write_account_c
798 ])
799 );
800 sync_finalize_priority_fee_for_test(&prioritization_fee_cache, 2, bank2.bank_id());
802 assert_eq!(
803 vec![(1, 1_000_000), (2, 3_000_000)],
804 prioritization_fee_cache.get_prioritization_fees(&[]),
805 );
806 assert_eq!(
807 vec![(1, 2_000_000), (2, 3_000_000)],
808 prioritization_fee_cache.get_prioritization_fees(&[write_account_a]),
809 );
810 assert_eq!(
811 vec![(1, 2_000_000), (2, 4_000_000)],
812 prioritization_fee_cache.get_prioritization_fees(&[write_account_b]),
813 );
814 assert_eq!(
815 vec![(1, 1_000_000), (2, 4_000_000)],
816 prioritization_fee_cache.get_prioritization_fees(&[write_account_c]),
817 );
818 assert_eq!(
819 vec![(1, 2_000_000), (2, 4_000_000)],
820 prioritization_fee_cache
821 .get_prioritization_fees(&[write_account_a, write_account_b]),
822 );
823 assert_eq!(
824 vec![(1, 2_000_000), (2, 4_000_000)],
825 prioritization_fee_cache.get_prioritization_fees(&[
826 write_account_a,
827 write_account_b,
828 write_account_c,
829 ]),
830 );
831 }
832
833 {
835 let txs = [
836 build_sanitized_transaction_for_test(6_000_000, &write_account_a, &write_account_c),
837 build_sanitized_transaction_for_test(
838 5_000_000,
839 &Pubkey::new_unique(),
840 &Pubkey::new_unique(),
841 ),
842 ];
843 sync_update(&prioritization_fee_cache, bank3.clone(), txs.iter());
844 assert_eq!(
846 vec![(1, 1_000_000), (2, 3_000_000)],
847 prioritization_fee_cache.get_prioritization_fees(&[]),
848 );
849 assert_eq!(
850 vec![(1, 2_000_000), (2, 3_000_000)],
851 prioritization_fee_cache.get_prioritization_fees(&[write_account_a]),
852 );
853 assert_eq!(
854 vec![(1, 2_000_000), (2, 4_000_000)],
855 prioritization_fee_cache.get_prioritization_fees(&[write_account_b]),
856 );
857 assert_eq!(
858 vec![(1, 1_000_000), (2, 4_000_000)],
859 prioritization_fee_cache.get_prioritization_fees(&[write_account_c]),
860 );
861 assert_eq!(
862 vec![(1, 2_000_000), (2, 4_000_000)],
863 prioritization_fee_cache
864 .get_prioritization_fees(&[write_account_a, write_account_b]),
865 );
866 assert_eq!(
867 vec![(1, 2_000_000), (2, 4_000_000)],
868 prioritization_fee_cache.get_prioritization_fees(&[
869 write_account_a,
870 write_account_b,
871 write_account_c,
872 ]),
873 );
874 sync_finalize_priority_fee_for_test(&prioritization_fee_cache, 3, bank3.bank_id());
876 assert_eq!(
877 vec![(1, 1_000_000), (2, 3_000_000), (3, 5_000_000)],
878 prioritization_fee_cache.get_prioritization_fees(&[]),
879 );
880 assert_eq!(
881 vec![(1, 2_000_000), (2, 3_000_000), (3, 6_000_000)],
882 prioritization_fee_cache.get_prioritization_fees(&[write_account_a]),
883 );
884 assert_eq!(
885 vec![(1, 2_000_000), (2, 4_000_000), (3, 5_000_000)],
886 prioritization_fee_cache.get_prioritization_fees(&[write_account_b]),
887 );
888 assert_eq!(
889 vec![(1, 1_000_000), (2, 4_000_000), (3, 6_000_000)],
890 prioritization_fee_cache.get_prioritization_fees(&[write_account_c]),
891 );
892 assert_eq!(
893 vec![(1, 2_000_000), (2, 4_000_000), (3, 6_000_000)],
894 prioritization_fee_cache
895 .get_prioritization_fees(&[write_account_a, write_account_b]),
896 );
897 assert_eq!(
898 vec![(1, 2_000_000), (2, 4_000_000), (3, 6_000_000)],
899 prioritization_fee_cache.get_prioritization_fees(&[
900 write_account_a,
901 write_account_b,
902 write_account_c,
903 ]),
904 );
905 }
906 }
907
908 #[test]
909 fn test_purge_duplicated_bank() {
910 agave_logger::setup();
913 let write_account_a = Pubkey::new_unique();
914 let write_account_b = Pubkey::new_unique();
915 let write_account_c = Pubkey::new_unique();
916
917 let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(10_000);
918 let bank0 = Bank::new_for_benches(&genesis_config);
919 let bank_forks = BankForks::new_rw_arc(bank0);
920 let bank = bank_forks.read().unwrap().working_bank();
921 let leader = SlotLeader::new_unique();
922 let slot: Slot = 999;
923 let bank1 = Arc::new(Bank::new_from_parent(bank.clone(), leader, slot));
924 let bank2 = Arc::new(Bank::new_from_parent(bank, leader, slot + 1));
925
926 let prioritization_fee_cache = PrioritizationFeeCache::default();
927
928 {
930 let txs = [
931 build_sanitized_transaction_for_test(2_000_000, &write_account_a, &write_account_b),
932 build_sanitized_transaction_for_test(
933 1_000_000,
934 &Pubkey::new_unique(),
935 &Pubkey::new_unique(),
936 ),
937 ];
938 sync_update(&prioritization_fee_cache, bank1.clone(), txs.iter());
939 }
940
941 {
943 let txs = [
944 build_sanitized_transaction_for_test(4, &write_account_b, &write_account_c),
945 build_sanitized_transaction_for_test(
946 3,
947 &Pubkey::new_unique(),
948 &Pubkey::new_unique(),
949 ),
950 ];
951 sync_update(&prioritization_fee_cache, bank2, txs.iter());
952 }
953
954 {
956 sync_finalize_priority_fee_for_test(&prioritization_fee_cache, slot, bank1.bank_id());
957
958 assert_eq!(
960 vec![(slot, 1_000_000)],
961 prioritization_fee_cache.get_prioritization_fees(&[])
962 );
963 assert_eq!(
964 vec![(slot, 2_000_000)],
965 prioritization_fee_cache.get_prioritization_fees(&[write_account_a])
966 );
967 assert_eq!(
968 vec![(slot, 2_000_000)],
969 prioritization_fee_cache.get_prioritization_fees(&[write_account_b])
970 );
971 assert_eq!(
972 vec![(slot, 1_000_000)],
973 prioritization_fee_cache.get_prioritization_fees(&[write_account_c])
974 );
975 assert_eq!(
976 vec![(slot, 2_000_000)],
977 prioritization_fee_cache
978 .get_prioritization_fees(&[write_account_a, write_account_b])
979 );
980 assert_eq!(
981 vec![(slot, 2_000_000)],
982 prioritization_fee_cache.get_prioritization_fees(&[
983 write_account_a,
984 write_account_b,
985 write_account_c
986 ])
987 );
988 }
989 }
990}