solana_runtime/
prioritization_fee_cache.rs

1use {
2    crate::{
3        bank::Bank, prioritization_fee::*,
4        transaction_priority_details::GetTransactionPriorityDetails,
5    },
6    crossbeam_channel::{unbounded, Receiver, Sender},
7    log::*,
8    lru::LruCache,
9    safecoin_measure::measure,
10    solana_sdk::{
11        clock::Slot, pubkey::Pubkey, saturating_add_assign, transaction::SanitizedTransaction,
12    },
13    std::{
14        collections::HashMap,
15        sync::{
16            atomic::{AtomicU64, Ordering},
17            Arc, Mutex, RwLock,
18        },
19        thread::{Builder, JoinHandle},
20    },
21};
22
23/// The maximum number of blocks to keep in `PrioritizationFeeCache`, ie.
24/// the amount of history generally desired to estimate the prioritization fee needed to
25/// land a transaction in the current block.
26const MAX_NUM_RECENT_BLOCKS: u64 = 150;
27
28#[derive(Debug, Default)]
29struct PrioritizationFeeCacheMetrics {
30    // Count of transactions that successfully updated each slot's prioritization fee cache.
31    successful_transaction_update_count: AtomicU64,
32
33    // Accumulated time spent on tracking prioritization fee for each slot.
34    total_update_elapsed_us: AtomicU64,
35
36    // Accumulated time spent on acquiring cache write lock.
37    total_cache_lock_elapsed_us: AtomicU64,
38
39    // Accumulated time spent on acquiring each block entry's lock..
40    total_entry_lock_elapsed_us: AtomicU64,
41
42    // Accumulated time spent on updating block prioritization fees.
43    total_entry_update_elapsed_us: AtomicU64,
44
45    // Accumulated time spent on finalizing block prioritization fees.
46    total_block_finalize_elapsed_us: AtomicU64,
47}
48
49impl PrioritizationFeeCacheMetrics {
50    fn accumulate_successful_transaction_update_count(&self, val: u64) {
51        self.successful_transaction_update_count
52            .fetch_add(val, Ordering::Relaxed);
53    }
54
55    fn accumulate_total_update_elapsed_us(&self, val: u64) {
56        self.total_update_elapsed_us
57            .fetch_add(val, Ordering::Relaxed);
58    }
59
60    fn accumulate_total_cache_lock_elapsed_us(&self, val: u64) {
61        self.total_cache_lock_elapsed_us
62            .fetch_add(val, Ordering::Relaxed);
63    }
64
65    fn accumulate_total_entry_lock_elapsed_us(&self, val: u64) {
66        self.total_entry_lock_elapsed_us
67            .fetch_add(val, Ordering::Relaxed);
68    }
69
70    fn accumulate_total_entry_update_elapsed_us(&self, val: u64) {
71        self.total_entry_update_elapsed_us
72            .fetch_add(val, Ordering::Relaxed);
73    }
74
75    fn accumulate_total_block_finalize_elapsed_us(&self, val: u64) {
76        self.total_block_finalize_elapsed_us
77            .fetch_add(val, Ordering::Relaxed);
78    }
79
80    fn report(&self, slot: Slot) {
81        datapoint_info!(
82            "block_prioritization_fee_counters",
83            ("slot", slot as i64, i64),
84            (
85                "successful_transaction_update_count",
86                self.successful_transaction_update_count
87                    .swap(0, Ordering::Relaxed) as i64,
88                i64
89            ),
90            (
91                "total_update_elapsed_us",
92                self.total_update_elapsed_us.swap(0, Ordering::Relaxed) as i64,
93                i64
94            ),
95            (
96                "total_cache_lock_elapsed_us",
97                self.total_cache_lock_elapsed_us.swap(0, Ordering::Relaxed) as i64,
98                i64
99            ),
100            (
101                "total_entry_lock_elapsed_us",
102                self.total_entry_lock_elapsed_us.swap(0, Ordering::Relaxed) as i64,
103                i64
104            ),
105            (
106                "total_entry_update_elapsed_us",
107                self.total_entry_update_elapsed_us
108                    .swap(0, Ordering::Relaxed) as i64,
109                i64
110            ),
111            (
112                "total_block_finalize_elapsed_us",
113                self.total_block_finalize_elapsed_us
114                    .swap(0, Ordering::Relaxed) as i64,
115                i64
116            ),
117        );
118    }
119}
120
121enum CacheServiceUpdate {
122    TransactionUpdate {
123        slot: Slot,
124        transaction_fee: u64,
125        writable_accounts: Arc<Vec<Pubkey>>,
126    },
127    BankFrozen {
128        slot: Slot,
129    },
130    Exit,
131}
132
133/// Stores up to MAX_NUM_RECENT_BLOCKS recent block's prioritization fee,
134/// A separate internal thread `service_thread` handles additional tasks when a bank is frozen,
135/// and collecting stats and reporting metrics.
136pub struct PrioritizationFeeCache {
137    cache: Arc<RwLock<LruCache<Slot, Arc<Mutex<PrioritizationFee>>>>>,
138    service_thread: Option<JoinHandle<()>>,
139    sender: Sender<CacheServiceUpdate>,
140    metrics: Arc<PrioritizationFeeCacheMetrics>,
141}
142
143impl Default for PrioritizationFeeCache {
144    fn default() -> Self {
145        Self::new(MAX_NUM_RECENT_BLOCKS)
146    }
147}
148
149impl Drop for PrioritizationFeeCache {
150    fn drop(&mut self) {
151        let _ = self.sender.send(CacheServiceUpdate::Exit);
152        self.service_thread
153            .take()
154            .unwrap()
155            .join()
156            .expect("Prioritization fee cache servicing thread failed to join");
157    }
158}
159
160impl PrioritizationFeeCache {
161    pub fn new(capacity: u64) -> Self {
162        let metrics = Arc::new(PrioritizationFeeCacheMetrics::default());
163        let (sender, receiver) = unbounded();
164        let cache = Arc::new(RwLock::new(LruCache::new(capacity as usize)));
165
166        let cache_clone = cache.clone();
167        let metrics_clone = metrics.clone();
168        let service_thread = Some(
169            Builder::new()
170                .name("prioritization-fee-cache-servicing-thread".to_string())
171                .spawn(move || {
172                    Self::service_loop(cache_clone, receiver, metrics_clone);
173                })
174                .unwrap(),
175        );
176
177        PrioritizationFeeCache {
178            cache,
179            service_thread,
180            sender,
181            metrics,
182        }
183    }
184
185    /// Get prioritization fee entry, create new entry if necessary
186    fn get_prioritization_fee(
187        cache: Arc<RwLock<LruCache<Slot, Arc<Mutex<PrioritizationFee>>>>>,
188        slot: &Slot,
189    ) -> Arc<Mutex<PrioritizationFee>> {
190        let mut cache = cache.write().unwrap();
191        match cache.get(slot) {
192            Some(entry) => Arc::clone(entry),
193            None => {
194                let entry = Arc::new(Mutex::new(PrioritizationFee::default()));
195                cache.put(*slot, Arc::clone(&entry));
196                entry
197            }
198        }
199    }
200
201    /// Update with a list of transactions' tx_priority_details and tx_account_locks; Only
202    /// transactions have both valid priority_detail and account_locks will be used to update
203    /// fee_cache asynchronously.
204    pub fn update<'a>(&self, bank: Arc<Bank>, txs: impl Iterator<Item = &'a SanitizedTransaction>) {
205        let mut successful_transaction_update_count: u64 = 0;
206        let (_, send_updates_time) = measure!(
207            {
208                for sanitized_transaction in txs {
209                    let priority_details = sanitized_transaction.get_transaction_priority_details();
210                    let account_locks = sanitized_transaction
211                        .get_account_locks(bank.get_transaction_account_lock_limit());
212
213                    if priority_details.is_none() || account_locks.is_err() {
214                        continue;
215                    }
216                    let priority_details = priority_details.unwrap();
217
218                    // filter out any transaction that requests zero compute_unit_limit
219                    // since its priority fee amount is not instructive
220                    if priority_details.compute_unit_limit == 0 {
221                        continue;
222                    }
223
224                    let writable_accounts = Arc::new(
225                        account_locks
226                            .unwrap()
227                            .writable
228                            .iter()
229                            .map(|key| **key)
230                            .collect::<Vec<_>>(),
231                    );
232
233                    self.sender
234                        .send(CacheServiceUpdate::TransactionUpdate {
235                            slot: bank.slot(),
236                            transaction_fee: priority_details.priority,
237                            writable_accounts,
238                        })
239                        .unwrap_or_else(|err| {
240                            warn!(
241                                "prioritization fee cache transaction updates failed: {:?}",
242                                err
243                            );
244                        });
245                    saturating_add_assign!(successful_transaction_update_count, 1)
246                }
247            },
248            "send_updates",
249        );
250
251        self.metrics
252            .accumulate_total_update_elapsed_us(send_updates_time.as_us());
253        self.metrics
254            .accumulate_successful_transaction_update_count(successful_transaction_update_count);
255    }
256
257    /// Finalize prioritization fee when it's bank is completely replayed from blockstore,
258    /// by pruning irrelevant accounts to save space, and marking its availability for queries.
259    pub fn finalize_priority_fee(&self, slot: Slot) {
260        self.sender
261            .send(CacheServiceUpdate::BankFrozen { slot })
262            .unwrap_or_else(|err| {
263                warn!(
264                    "prioritization fee cache signalling bank frozen failed: {:?}",
265                    err
266                )
267            });
268    }
269
270    /// Internal function is invoked by worker thread to update slot's minimum prioritization fee,
271    /// Cache lock contends here.
272    fn update_cache(
273        cache: Arc<RwLock<LruCache<Slot, Arc<Mutex<PrioritizationFee>>>>>,
274        slot: &Slot,
275        transaction_fee: u64,
276        writable_accounts: Arc<Vec<Pubkey>>,
277        metrics: Arc<PrioritizationFeeCacheMetrics>,
278    ) {
279        let (block_prioritization_fee, cache_lock_time) =
280            measure!(Self::get_prioritization_fee(cache, slot), "cache_lock_time");
281
282        let (mut block_prioritization_fee, entry_lock_time) =
283            measure!(block_prioritization_fee.lock().unwrap(), "entry_lock_time");
284
285        let (_, entry_update_time) = measure!(
286            block_prioritization_fee.update(transaction_fee, &writable_accounts),
287            "entry_update_time"
288        );
289        metrics.accumulate_total_cache_lock_elapsed_us(cache_lock_time.as_us());
290        metrics.accumulate_total_entry_lock_elapsed_us(entry_lock_time.as_us());
291        metrics.accumulate_total_entry_update_elapsed_us(entry_update_time.as_us());
292    }
293
294    fn finalize_slot(
295        cache: Arc<RwLock<LruCache<Slot, Arc<Mutex<PrioritizationFee>>>>>,
296        slot: &Slot,
297        metrics: Arc<PrioritizationFeeCacheMetrics>,
298    ) {
299        let (block_prioritization_fee, cache_lock_time) =
300            measure!(Self::get_prioritization_fee(cache, slot), "cache_lock_time");
301
302        let (mut block_prioritization_fee, entry_lock_time) =
303            measure!(block_prioritization_fee.lock().unwrap(), "entry_lock_time");
304
305        // prune cache by evicting write account entry from prioritization fee if its fee is less
306        // or equal to block's minimum transaction fee, because they are irrelevant in calculating
307        // block minimum fee.
308        let (_, slot_finalize_time) = measure!(
309            block_prioritization_fee.mark_block_completed(),
310            "slot_finalize_time"
311        );
312        block_prioritization_fee.report_metrics(*slot);
313        metrics.accumulate_total_cache_lock_elapsed_us(cache_lock_time.as_us());
314        metrics.accumulate_total_entry_lock_elapsed_us(entry_lock_time.as_us());
315        metrics.accumulate_total_block_finalize_elapsed_us(slot_finalize_time.as_us());
316    }
317
318    fn service_loop(
319        cache: Arc<RwLock<LruCache<Slot, Arc<Mutex<PrioritizationFee>>>>>,
320        receiver: Receiver<CacheServiceUpdate>,
321        metrics: Arc<PrioritizationFeeCacheMetrics>,
322    ) {
323        for update in receiver.iter() {
324            match update {
325                CacheServiceUpdate::TransactionUpdate {
326                    slot,
327                    transaction_fee,
328                    writable_accounts,
329                } => Self::update_cache(
330                    cache.clone(),
331                    &slot,
332                    transaction_fee,
333                    writable_accounts,
334                    metrics.clone(),
335                ),
336                CacheServiceUpdate::BankFrozen { slot } => {
337                    Self::finalize_slot(cache.clone(), &slot, metrics.clone());
338
339                    metrics.report(slot);
340                }
341                CacheServiceUpdate::Exit => {
342                    break;
343                }
344            }
345        }
346    }
347
348    /// Returns number of blocks that have finalized minimum fees collection
349    pub fn available_block_count(&self) -> usize {
350        self.cache
351            .read()
352            .unwrap()
353            .iter()
354            .filter(|(_slot, prioritization_fee)| prioritization_fee.lock().unwrap().is_finalized())
355            .count()
356    }
357
358    pub fn get_prioritization_fees(&self, account_keys: &[Pubkey]) -> HashMap<Slot, u64> {
359        self.cache
360            .read()
361            .unwrap()
362            .iter()
363            .filter_map(|(slot, prioritization_fee)| {
364                let prioritization_fee_read = prioritization_fee.lock().unwrap();
365                prioritization_fee_read.is_finalized().then(|| {
366                    let mut fee = prioritization_fee_read
367                        .get_min_transaction_fee()
368                        .unwrap_or_default();
369                    for account_key in account_keys {
370                        if let Some(account_fee) =
371                            prioritization_fee_read.get_writable_account_fee(account_key)
372                        {
373                            fee = std::cmp::max(fee, account_fee);
374                        }
375                    }
376                    Some((*slot, fee))
377                })
378            })
379            .flatten()
380            .collect()
381    }
382}
383
384#[cfg(test)]
385mod tests {
386    use {
387        super::*,
388        crate::{
389            bank::Bank,
390            bank_forks::BankForks,
391            genesis_utils::{create_genesis_config, GenesisConfigInfo},
392        },
393        solana_sdk::{
394            compute_budget::ComputeBudgetInstruction,
395            message::Message,
396            pubkey::Pubkey,
397            system_instruction,
398            transaction::{SanitizedTransaction, Transaction},
399        },
400    };
401
402    fn build_sanitized_transaction_for_test(
403        compute_unit_price: u64,
404        signer_account: &Pubkey,
405        write_account: &Pubkey,
406    ) -> SanitizedTransaction {
407        let transaction = Transaction::new_unsigned(Message::new(
408            &[
409                system_instruction::transfer(signer_account, write_account, 1),
410                ComputeBudgetInstruction::set_compute_unit_price(compute_unit_price),
411            ],
412            Some(signer_account),
413        ));
414
415        SanitizedTransaction::try_from_legacy_transaction(transaction).unwrap()
416    }
417
418    // update fee cache is asynchronous, this test helper blocks until update is completed.
419    fn sync_update<'a>(
420        prioritization_fee_cache: &mut PrioritizationFeeCache,
421        bank: Arc<Bank>,
422        txs: impl Iterator<Item = &'a SanitizedTransaction>,
423    ) {
424        prioritization_fee_cache.update(bank.clone(), txs);
425
426        let block_fee = PrioritizationFeeCache::get_prioritization_fee(
427            prioritization_fee_cache.cache.clone(),
428            &bank.slot(),
429        );
430
431        // wait till update is done
432        while block_fee
433            .lock()
434            .unwrap()
435            .get_min_transaction_fee()
436            .is_none()
437        {
438            std::thread::sleep(std::time::Duration::from_millis(100));
439        }
440    }
441
442    // finalization is asynchronous, this test helper blocks until finalization is completed.
443    fn sync_finalize_priority_fee_for_test(
444        prioritization_fee_cache: &mut PrioritizationFeeCache,
445        slot: Slot,
446    ) {
447        prioritization_fee_cache.finalize_priority_fee(slot);
448        let fee = PrioritizationFeeCache::get_prioritization_fee(
449            prioritization_fee_cache.cache.clone(),
450            &slot,
451        );
452
453        // wait till finalization is done
454        while !fee.lock().unwrap().is_finalized() {
455            std::thread::sleep(std::time::Duration::from_millis(100));
456        }
457    }
458
459    #[test]
460    fn test_prioritization_fee_cache_update() {
461        solana_logger::setup();
462        let write_account_a = Pubkey::new_unique();
463        let write_account_b = Pubkey::new_unique();
464        let write_account_c = Pubkey::new_unique();
465
466        // Set up test with 3 transactions, in format of [fee, write-accounts...],
467        // Shall expect fee cache is updated in following sequence:
468        // transaction                    block minimum prioritization fee cache
469        // [fee, write_accounts...]  -->  [block, account_a, account_b, account_c]
470        // -----------------------------------------------------------------------
471        // [5,   a, b             ]  -->  [5,     5,         5,         nil      ]
472        // [9,      b, c          ]  -->  [5,     5,         5,         9        ]
473        // [2,   a,    c          ]  -->  [2,     2,         5,         2        ]
474        //
475        let txs = vec![
476            build_sanitized_transaction_for_test(5, &write_account_a, &write_account_b),
477            build_sanitized_transaction_for_test(9, &write_account_b, &write_account_c),
478            build_sanitized_transaction_for_test(2, &write_account_a, &write_account_c),
479        ];
480
481        let bank = Arc::new(Bank::default_for_tests());
482        let slot = bank.slot();
483
484        let mut prioritization_fee_cache = PrioritizationFeeCache::default();
485        sync_update(&mut prioritization_fee_cache, bank, txs.iter());
486
487        // assert block minimum fee and account a, b, c fee accordingly
488        {
489            let fee = PrioritizationFeeCache::get_prioritization_fee(
490                prioritization_fee_cache.cache.clone(),
491                &slot,
492            );
493            let fee = fee.lock().unwrap();
494            assert_eq!(2, fee.get_min_transaction_fee().unwrap());
495            assert_eq!(2, fee.get_writable_account_fee(&write_account_a).unwrap());
496            assert_eq!(5, fee.get_writable_account_fee(&write_account_b).unwrap());
497            assert_eq!(2, fee.get_writable_account_fee(&write_account_c).unwrap());
498            // assert unknown account d fee
499            assert!(fee
500                .get_writable_account_fee(&Pubkey::new_unique())
501                .is_none());
502        }
503
504        // assert after prune, account a and c should be removed from cache to save space
505        {
506            sync_finalize_priority_fee_for_test(&mut prioritization_fee_cache, slot);
507            let fee = PrioritizationFeeCache::get_prioritization_fee(
508                prioritization_fee_cache.cache.clone(),
509                &slot,
510            );
511            let fee = fee.lock().unwrap();
512            assert_eq!(2, fee.get_min_transaction_fee().unwrap());
513            assert!(fee.get_writable_account_fee(&write_account_a).is_none());
514            assert_eq!(5, fee.get_writable_account_fee(&write_account_b).unwrap());
515            assert!(fee.get_writable_account_fee(&write_account_c).is_none());
516        }
517    }
518
519    #[test]
520    fn test_available_block_count() {
521        let prioritization_fee_cache = PrioritizationFeeCache::default();
522
523        assert!(PrioritizationFeeCache::get_prioritization_fee(
524            prioritization_fee_cache.cache.clone(),
525            &1
526        )
527        .lock()
528        .unwrap()
529        .mark_block_completed()
530        .is_ok());
531        assert!(PrioritizationFeeCache::get_prioritization_fee(
532            prioritization_fee_cache.cache.clone(),
533            &2
534        )
535        .lock()
536        .unwrap()
537        .mark_block_completed()
538        .is_ok());
539        // add slot 3 entry to cache, but not finalize it
540        PrioritizationFeeCache::get_prioritization_fee(prioritization_fee_cache.cache.clone(), &3);
541
542        // assert available block count should be 2 finalized blocks
543        assert_eq!(2, prioritization_fee_cache.available_block_count());
544    }
545
546    fn hashmap_of(vec: Vec<(Slot, u64)>) -> HashMap<Slot, u64> {
547        vec.into_iter().collect()
548    }
549
550    #[test]
551    fn test_get_prioritization_fees() {
552        solana_logger::setup();
553        let write_account_a = Pubkey::new_unique();
554        let write_account_b = Pubkey::new_unique();
555        let write_account_c = Pubkey::new_unique();
556
557        let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(10_000);
558        let bank0 = Bank::new_for_benches(&genesis_config);
559        let bank_forks = BankForks::new(bank0);
560        let bank = bank_forks.working_bank();
561        let collector = solana_sdk::pubkey::new_rand();
562        let bank1 = Arc::new(Bank::new_from_parent(&bank, &collector, 1));
563        let bank2 = Arc::new(Bank::new_from_parent(&bank, &collector, 2));
564        let bank3 = Arc::new(Bank::new_from_parent(&bank, &collector, 3));
565
566        let mut prioritization_fee_cache = PrioritizationFeeCache::default();
567
568        // Assert no minimum fee from empty cache
569        assert!(prioritization_fee_cache
570            .get_prioritization_fees(&[])
571            .is_empty());
572        assert!(prioritization_fee_cache
573            .get_prioritization_fees(&[write_account_a])
574            .is_empty());
575        assert!(prioritization_fee_cache
576            .get_prioritization_fees(&[write_account_b])
577            .is_empty());
578        assert!(prioritization_fee_cache
579            .get_prioritization_fees(&[write_account_c])
580            .is_empty());
581        assert!(prioritization_fee_cache
582            .get_prioritization_fees(&[write_account_a, write_account_b])
583            .is_empty());
584        assert!(prioritization_fee_cache
585            .get_prioritization_fees(&[write_account_a, write_account_b, write_account_c])
586            .is_empty());
587
588        // Assert after add one transaction for slot 1
589        {
590            let txs = vec![
591                build_sanitized_transaction_for_test(2, &write_account_a, &write_account_b),
592                build_sanitized_transaction_for_test(
593                    1,
594                    &Pubkey::new_unique(),
595                    &Pubkey::new_unique(),
596                ),
597            ];
598            sync_update(&mut prioritization_fee_cache, bank1, txs.iter());
599            // before block is marked as completed
600            assert!(prioritization_fee_cache
601                .get_prioritization_fees(&[])
602                .is_empty());
603            assert!(prioritization_fee_cache
604                .get_prioritization_fees(&[write_account_a])
605                .is_empty());
606            assert!(prioritization_fee_cache
607                .get_prioritization_fees(&[write_account_b])
608                .is_empty());
609            assert!(prioritization_fee_cache
610                .get_prioritization_fees(&[write_account_c])
611                .is_empty());
612            assert!(prioritization_fee_cache
613                .get_prioritization_fees(&[write_account_a, write_account_b])
614                .is_empty());
615            assert!(prioritization_fee_cache
616                .get_prioritization_fees(&[write_account_a, write_account_b, write_account_c])
617                .is_empty());
618            // after block is completed
619            sync_finalize_priority_fee_for_test(&mut prioritization_fee_cache, 1);
620            assert_eq!(
621                hashmap_of(vec![(1, 1)]),
622                prioritization_fee_cache.get_prioritization_fees(&[])
623            );
624            assert_eq!(
625                hashmap_of(vec![(1, 2)]),
626                prioritization_fee_cache.get_prioritization_fees(&[write_account_a])
627            );
628            assert_eq!(
629                hashmap_of(vec![(1, 2)]),
630                prioritization_fee_cache.get_prioritization_fees(&[write_account_b])
631            );
632            assert_eq!(
633                hashmap_of(vec![(1, 1)]),
634                prioritization_fee_cache.get_prioritization_fees(&[write_account_c])
635            );
636            assert_eq!(
637                hashmap_of(vec![(1, 2)]),
638                prioritization_fee_cache
639                    .get_prioritization_fees(&[write_account_a, write_account_b])
640            );
641            assert_eq!(
642                hashmap_of(vec![(1, 2)]),
643                prioritization_fee_cache.get_prioritization_fees(&[
644                    write_account_a,
645                    write_account_b,
646                    write_account_c
647                ])
648            );
649        }
650
651        // Assert after add one transaction for slot 2
652        {
653            let txs = vec![
654                build_sanitized_transaction_for_test(4, &write_account_b, &write_account_c),
655                build_sanitized_transaction_for_test(
656                    3,
657                    &Pubkey::new_unique(),
658                    &Pubkey::new_unique(),
659                ),
660            ];
661            sync_update(&mut prioritization_fee_cache, bank2, txs.iter());
662            // before block is marked as completed
663            assert_eq!(
664                hashmap_of(vec![(1, 1)]),
665                prioritization_fee_cache.get_prioritization_fees(&[])
666            );
667            assert_eq!(
668                hashmap_of(vec![(1, 2)]),
669                prioritization_fee_cache.get_prioritization_fees(&[write_account_a])
670            );
671            assert_eq!(
672                hashmap_of(vec![(1, 2)]),
673                prioritization_fee_cache.get_prioritization_fees(&[write_account_b])
674            );
675            assert_eq!(
676                hashmap_of(vec![(1, 1)]),
677                prioritization_fee_cache.get_prioritization_fees(&[write_account_c])
678            );
679            assert_eq!(
680                hashmap_of(vec![(1, 2)]),
681                prioritization_fee_cache
682                    .get_prioritization_fees(&[write_account_a, write_account_b])
683            );
684            assert_eq!(
685                hashmap_of(vec![(1, 2)]),
686                prioritization_fee_cache.get_prioritization_fees(&[
687                    write_account_a,
688                    write_account_b,
689                    write_account_c
690                ])
691            );
692            // after block is completed
693            sync_finalize_priority_fee_for_test(&mut prioritization_fee_cache, 2);
694            assert_eq!(
695                hashmap_of(vec![(2, 3), (1, 1)]),
696                prioritization_fee_cache.get_prioritization_fees(&[]),
697            );
698            assert_eq!(
699                hashmap_of(vec![(2, 3), (1, 2)]),
700                prioritization_fee_cache.get_prioritization_fees(&[write_account_a]),
701            );
702            assert_eq!(
703                hashmap_of(vec![(2, 4), (1, 2)]),
704                prioritization_fee_cache.get_prioritization_fees(&[write_account_b]),
705            );
706            assert_eq!(
707                hashmap_of(vec![(2, 4), (1, 1)]),
708                prioritization_fee_cache.get_prioritization_fees(&[write_account_c]),
709            );
710            assert_eq!(
711                hashmap_of(vec![(2, 4), (1, 2)]),
712                prioritization_fee_cache
713                    .get_prioritization_fees(&[write_account_a, write_account_b]),
714            );
715            assert_eq!(
716                hashmap_of(vec![(2, 4), (1, 2)]),
717                prioritization_fee_cache.get_prioritization_fees(&[
718                    write_account_a,
719                    write_account_b,
720                    write_account_c,
721                ]),
722            );
723        }
724
725        // Assert after add one transaction for slot 3
726        {
727            let txs = vec![
728                build_sanitized_transaction_for_test(6, &write_account_a, &write_account_c),
729                build_sanitized_transaction_for_test(
730                    5,
731                    &Pubkey::new_unique(),
732                    &Pubkey::new_unique(),
733                ),
734            ];
735            sync_update(&mut prioritization_fee_cache, bank3, txs.iter());
736            // before block is marked as completed
737            assert_eq!(
738                hashmap_of(vec![(2, 3), (1, 1)]),
739                prioritization_fee_cache.get_prioritization_fees(&[]),
740            );
741            assert_eq!(
742                hashmap_of(vec![(2, 3), (1, 2)]),
743                prioritization_fee_cache.get_prioritization_fees(&[write_account_a]),
744            );
745            assert_eq!(
746                hashmap_of(vec![(2, 4), (1, 2)]),
747                prioritization_fee_cache.get_prioritization_fees(&[write_account_b]),
748            );
749            assert_eq!(
750                hashmap_of(vec![(2, 4), (1, 1)]),
751                prioritization_fee_cache.get_prioritization_fees(&[write_account_c]),
752            );
753            assert_eq!(
754                hashmap_of(vec![(2, 4), (1, 2)]),
755                prioritization_fee_cache
756                    .get_prioritization_fees(&[write_account_a, write_account_b]),
757            );
758            assert_eq!(
759                hashmap_of(vec![(2, 4), (1, 2)]),
760                prioritization_fee_cache.get_prioritization_fees(&[
761                    write_account_a,
762                    write_account_b,
763                    write_account_c,
764                ]),
765            );
766            // after block is completed
767            sync_finalize_priority_fee_for_test(&mut prioritization_fee_cache, 3);
768            assert_eq!(
769                hashmap_of(vec![(3, 5), (2, 3), (1, 1)]),
770                prioritization_fee_cache.get_prioritization_fees(&[]),
771            );
772            assert_eq!(
773                hashmap_of(vec![(3, 6), (2, 3), (1, 2)]),
774                prioritization_fee_cache.get_prioritization_fees(&[write_account_a]),
775            );
776            assert_eq!(
777                hashmap_of(vec![(3, 5), (2, 4), (1, 2)]),
778                prioritization_fee_cache.get_prioritization_fees(&[write_account_b]),
779            );
780            assert_eq!(
781                hashmap_of(vec![(3, 6), (2, 4), (1, 1)]),
782                prioritization_fee_cache.get_prioritization_fees(&[write_account_c]),
783            );
784            assert_eq!(
785                hashmap_of(vec![(3, 6), (2, 4), (1, 2)]),
786                prioritization_fee_cache
787                    .get_prioritization_fees(&[write_account_a, write_account_b]),
788            );
789            assert_eq!(
790                hashmap_of(vec![(3, 6), (2, 4), (1, 2)]),
791                prioritization_fee_cache.get_prioritization_fees(&[
792                    write_account_a,
793                    write_account_b,
794                    write_account_c,
795                ]),
796            );
797        }
798    }
799}