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    solana_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 non-vote 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                    // Vote transactions are not prioritized, therefore they are excluded from
210                    // updating fee_cache.
211                    if sanitized_transaction.is_simple_vote_transaction() {
212                        continue;
213                    }
214
215                    let priority_details = sanitized_transaction.get_transaction_priority_details();
216                    let account_locks = sanitized_transaction
217                        .get_account_locks(bank.get_transaction_account_lock_limit());
218
219                    if priority_details.is_none() || account_locks.is_err() {
220                        continue;
221                    }
222
223                    let writable_accounts = Arc::new(
224                        account_locks
225                            .unwrap()
226                            .writable
227                            .iter()
228                            .map(|key| **key)
229                            .collect::<Vec<_>>(),
230                    );
231
232                    self.sender
233                        .send(CacheServiceUpdate::TransactionUpdate {
234                            slot: bank.slot(),
235                            transaction_fee: priority_details.unwrap().priority,
236                            writable_accounts,
237                        })
238                        .unwrap_or_else(|err| {
239                            warn!(
240                                "prioritization fee cache transaction updates failed: {:?}",
241                                err
242                            );
243                        });
244                    saturating_add_assign!(successful_transaction_update_count, 1)
245                }
246            },
247            "send_updates",
248        );
249
250        self.metrics
251            .accumulate_total_update_elapsed_us(send_updates_time.as_us());
252        self.metrics
253            .accumulate_successful_transaction_update_count(successful_transaction_update_count);
254    }
255
256    /// Finalize prioritization fee when it's bank is completely replayed from blockstore,
257    /// by pruning irrelevant accounts to save space, and marking its availability for queries.
258    pub fn finalize_priority_fee(&self, slot: Slot) {
259        self.sender
260            .send(CacheServiceUpdate::BankFrozen { slot })
261            .unwrap_or_else(|err| {
262                warn!(
263                    "prioritization fee cache signalling bank frozen failed: {:?}",
264                    err
265                )
266            });
267    }
268
269    /// Internal function is invoked by worker thread to update slot's minimum prioritization fee,
270    /// Cache lock contends here.
271    fn update_cache(
272        cache: Arc<RwLock<LruCache<Slot, Arc<Mutex<PrioritizationFee>>>>>,
273        slot: &Slot,
274        transaction_fee: u64,
275        writable_accounts: Arc<Vec<Pubkey>>,
276        metrics: Arc<PrioritizationFeeCacheMetrics>,
277    ) {
278        let (block_prioritization_fee, cache_lock_time) =
279            measure!(Self::get_prioritization_fee(cache, slot), "cache_lock_time");
280
281        let (mut block_prioritization_fee, entry_lock_time) =
282            measure!(block_prioritization_fee.lock().unwrap(), "entry_lock_time");
283
284        let (_, entry_update_time) = measure!(
285            block_prioritization_fee.update(transaction_fee, &writable_accounts),
286            "entry_update_time"
287        );
288        metrics.accumulate_total_cache_lock_elapsed_us(cache_lock_time.as_us());
289        metrics.accumulate_total_entry_lock_elapsed_us(entry_lock_time.as_us());
290        metrics.accumulate_total_entry_update_elapsed_us(entry_update_time.as_us());
291    }
292
293    fn finalize_slot(
294        cache: Arc<RwLock<LruCache<Slot, Arc<Mutex<PrioritizationFee>>>>>,
295        slot: &Slot,
296        metrics: Arc<PrioritizationFeeCacheMetrics>,
297    ) {
298        let (block_prioritization_fee, cache_lock_time) =
299            measure!(Self::get_prioritization_fee(cache, slot), "cache_lock_time");
300
301        let (mut block_prioritization_fee, entry_lock_time) =
302            measure!(block_prioritization_fee.lock().unwrap(), "entry_lock_time");
303
304        // prune cache by evicting write account entry from prioritization fee if its fee is less
305        // or equal to block's minimum transaction fee, because they are irrelevant in calculating
306        // block minimum fee.
307        let (_, slot_finalize_time) = measure!(
308            block_prioritization_fee.mark_block_completed(),
309            "slot_finalize_time"
310        );
311        block_prioritization_fee.report_metrics(*slot);
312        metrics.accumulate_total_cache_lock_elapsed_us(cache_lock_time.as_us());
313        metrics.accumulate_total_entry_lock_elapsed_us(entry_lock_time.as_us());
314        metrics.accumulate_total_block_finalize_elapsed_us(slot_finalize_time.as_us());
315    }
316
317    fn service_loop(
318        cache: Arc<RwLock<LruCache<Slot, Arc<Mutex<PrioritizationFee>>>>>,
319        receiver: Receiver<CacheServiceUpdate>,
320        metrics: Arc<PrioritizationFeeCacheMetrics>,
321    ) {
322        for update in receiver.iter() {
323            match update {
324                CacheServiceUpdate::TransactionUpdate {
325                    slot,
326                    transaction_fee,
327                    writable_accounts,
328                } => Self::update_cache(
329                    cache.clone(),
330                    &slot,
331                    transaction_fee,
332                    writable_accounts,
333                    metrics.clone(),
334                ),
335                CacheServiceUpdate::BankFrozen { slot } => {
336                    Self::finalize_slot(cache.clone(), &slot, metrics.clone());
337
338                    metrics.report(slot);
339                }
340                CacheServiceUpdate::Exit => {
341                    break;
342                }
343            }
344        }
345    }
346
347    /// Returns number of blocks that have finalized minimum fees collection
348    pub fn available_block_count(&self) -> usize {
349        self.cache
350            .read()
351            .unwrap()
352            .iter()
353            .filter(|(_slot, prioritization_fee)| prioritization_fee.lock().unwrap().is_finalized())
354            .count()
355    }
356
357    pub fn get_prioritization_fees(&self, account_keys: &[Pubkey]) -> HashMap<Slot, u64> {
358        self.cache
359            .read()
360            .unwrap()
361            .iter()
362            .filter_map(|(slot, prioritization_fee)| {
363                let prioritization_fee_read = prioritization_fee.lock().unwrap();
364                prioritization_fee_read.is_finalized().then(|| {
365                    let mut fee = prioritization_fee_read
366                        .get_min_transaction_fee()
367                        .unwrap_or_default();
368                    for account_key in account_keys {
369                        if let Some(account_fee) =
370                            prioritization_fee_read.get_writable_account_fee(account_key)
371                        {
372                            fee = std::cmp::max(fee, account_fee);
373                        }
374                    }
375                    Some((*slot, fee))
376                })
377            })
378            .flatten()
379            .collect()
380    }
381}
382
383#[cfg(test)]
384mod tests {
385    use {
386        super::*,
387        crate::{
388            bank::Bank,
389            bank_forks::BankForks,
390            genesis_utils::{create_genesis_config, GenesisConfigInfo},
391        },
392        solana_sdk::{
393            compute_budget::ComputeBudgetInstruction,
394            message::Message,
395            pubkey::Pubkey,
396            system_instruction,
397            transaction::{SanitizedTransaction, Transaction},
398        },
399    };
400
401    fn build_sanitized_transaction_for_test(
402        compute_unit_price: u64,
403        signer_account: &Pubkey,
404        write_account: &Pubkey,
405    ) -> SanitizedTransaction {
406        let transaction = Transaction::new_unsigned(Message::new(
407            &[
408                system_instruction::transfer(signer_account, write_account, 1),
409                ComputeBudgetInstruction::set_compute_unit_price(compute_unit_price),
410            ],
411            Some(signer_account),
412        ));
413
414        SanitizedTransaction::try_from_legacy_transaction(transaction).unwrap()
415    }
416
417    // update fee cache is asynchronous, this test helper blocks until update is completed.
418    fn sync_update<'a>(
419        prioritization_fee_cache: &mut PrioritizationFeeCache,
420        bank: Arc<Bank>,
421        txs: impl Iterator<Item = &'a SanitizedTransaction>,
422    ) {
423        prioritization_fee_cache.update(bank.clone(), txs);
424
425        let block_fee = PrioritizationFeeCache::get_prioritization_fee(
426            prioritization_fee_cache.cache.clone(),
427            &bank.slot(),
428        );
429
430        // wait till update is done
431        while block_fee
432            .lock()
433            .unwrap()
434            .get_min_transaction_fee()
435            .is_none()
436        {
437            std::thread::sleep(std::time::Duration::from_millis(100));
438        }
439    }
440
441    // finalization is asynchronous, this test helper blocks until finalization is completed.
442    fn sync_finalize_priority_fee_for_test(
443        prioritization_fee_cache: &mut PrioritizationFeeCache,
444        slot: Slot,
445    ) {
446        prioritization_fee_cache.finalize_priority_fee(slot);
447        let fee = PrioritizationFeeCache::get_prioritization_fee(
448            prioritization_fee_cache.cache.clone(),
449            &slot,
450        );
451
452        // wait till finalization is done
453        while !fee.lock().unwrap().is_finalized() {
454            std::thread::sleep(std::time::Duration::from_millis(100));
455        }
456    }
457
458    #[test]
459    fn test_prioritization_fee_cache_update() {
460        solana_logger::setup();
461        let write_account_a = Pubkey::new_unique();
462        let write_account_b = Pubkey::new_unique();
463        let write_account_c = Pubkey::new_unique();
464
465        // Set up test with 3 transactions, in format of [fee, write-accounts...],
466        // Shall expect fee cache is updated in following sequence:
467        // transaction                    block minimum prioritization fee cache
468        // [fee, write_accounts...]  -->  [block, account_a, account_b, account_c]
469        // -----------------------------------------------------------------------
470        // [5,   a, b             ]  -->  [5,     5,         5,         nil      ]
471        // [9,      b, c          ]  -->  [5,     5,         5,         9        ]
472        // [2,   a,    c          ]  -->  [2,     2,         5,         2        ]
473        //
474        let txs = vec![
475            build_sanitized_transaction_for_test(5, &write_account_a, &write_account_b),
476            build_sanitized_transaction_for_test(9, &write_account_b, &write_account_c),
477            build_sanitized_transaction_for_test(2, &write_account_a, &write_account_c),
478        ];
479
480        let bank = Arc::new(Bank::default_for_tests());
481        let slot = bank.slot();
482
483        let mut prioritization_fee_cache = PrioritizationFeeCache::default();
484        sync_update(&mut prioritization_fee_cache, bank, txs.iter());
485
486        // assert block minimum fee and account a, b, c fee accordingly
487        {
488            let fee = PrioritizationFeeCache::get_prioritization_fee(
489                prioritization_fee_cache.cache.clone(),
490                &slot,
491            );
492            let fee = fee.lock().unwrap();
493            assert_eq!(2, fee.get_min_transaction_fee().unwrap());
494            assert_eq!(2, fee.get_writable_account_fee(&write_account_a).unwrap());
495            assert_eq!(5, fee.get_writable_account_fee(&write_account_b).unwrap());
496            assert_eq!(2, fee.get_writable_account_fee(&write_account_c).unwrap());
497            // assert unknown account d fee
498            assert!(fee
499                .get_writable_account_fee(&Pubkey::new_unique())
500                .is_none());
501        }
502
503        // assert after prune, account a and c should be removed from cache to save space
504        {
505            sync_finalize_priority_fee_for_test(&mut prioritization_fee_cache, slot);
506            let fee = PrioritizationFeeCache::get_prioritization_fee(
507                prioritization_fee_cache.cache.clone(),
508                &slot,
509            );
510            let fee = fee.lock().unwrap();
511            assert_eq!(2, fee.get_min_transaction_fee().unwrap());
512            assert!(fee.get_writable_account_fee(&write_account_a).is_none());
513            assert_eq!(5, fee.get_writable_account_fee(&write_account_b).unwrap());
514            assert!(fee.get_writable_account_fee(&write_account_c).is_none());
515        }
516    }
517
518    #[test]
519    fn test_available_block_count() {
520        let prioritization_fee_cache = PrioritizationFeeCache::default();
521
522        assert!(PrioritizationFeeCache::get_prioritization_fee(
523            prioritization_fee_cache.cache.clone(),
524            &1
525        )
526        .lock()
527        .unwrap()
528        .mark_block_completed()
529        .is_ok());
530        assert!(PrioritizationFeeCache::get_prioritization_fee(
531            prioritization_fee_cache.cache.clone(),
532            &2
533        )
534        .lock()
535        .unwrap()
536        .mark_block_completed()
537        .is_ok());
538        // add slot 3 entry to cache, but not finalize it
539        PrioritizationFeeCache::get_prioritization_fee(prioritization_fee_cache.cache.clone(), &3);
540
541        // assert available block count should be 2 finalized blocks
542        assert_eq!(2, prioritization_fee_cache.available_block_count());
543    }
544
545    fn hashmap_of(vec: Vec<(Slot, u64)>) -> HashMap<Slot, u64> {
546        vec.into_iter().collect()
547    }
548
549    #[test]
550    fn test_get_prioritization_fees() {
551        solana_logger::setup();
552        let write_account_a = Pubkey::new_unique();
553        let write_account_b = Pubkey::new_unique();
554        let write_account_c = Pubkey::new_unique();
555
556        let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(10_000);
557        let bank0 = Bank::new_for_benches(&genesis_config);
558        let bank_forks = BankForks::new(bank0);
559        let bank = bank_forks.working_bank();
560        let collector = solana_sdk::pubkey::new_rand();
561        let bank1 = Arc::new(Bank::new_from_parent(&bank, &collector, 1));
562        let bank2 = Arc::new(Bank::new_from_parent(&bank, &collector, 2));
563        let bank3 = Arc::new(Bank::new_from_parent(&bank, &collector, 3));
564
565        let mut prioritization_fee_cache = PrioritizationFeeCache::default();
566
567        // Assert no minimum fee from empty cache
568        assert!(prioritization_fee_cache
569            .get_prioritization_fees(&[])
570            .is_empty());
571        assert!(prioritization_fee_cache
572            .get_prioritization_fees(&[write_account_a])
573            .is_empty());
574        assert!(prioritization_fee_cache
575            .get_prioritization_fees(&[write_account_b])
576            .is_empty());
577        assert!(prioritization_fee_cache
578            .get_prioritization_fees(&[write_account_c])
579            .is_empty());
580        assert!(prioritization_fee_cache
581            .get_prioritization_fees(&[write_account_a, write_account_b])
582            .is_empty());
583        assert!(prioritization_fee_cache
584            .get_prioritization_fees(&[write_account_a, write_account_b, write_account_c])
585            .is_empty());
586
587        // Assert after add one transaction for slot 1
588        {
589            let txs = vec![
590                build_sanitized_transaction_for_test(2, &write_account_a, &write_account_b),
591                build_sanitized_transaction_for_test(
592                    1,
593                    &Pubkey::new_unique(),
594                    &Pubkey::new_unique(),
595                ),
596            ];
597            sync_update(&mut prioritization_fee_cache, bank1, txs.iter());
598            // before block is marked as completed
599            assert!(prioritization_fee_cache
600                .get_prioritization_fees(&[])
601                .is_empty());
602            assert!(prioritization_fee_cache
603                .get_prioritization_fees(&[write_account_a])
604                .is_empty());
605            assert!(prioritization_fee_cache
606                .get_prioritization_fees(&[write_account_b])
607                .is_empty());
608            assert!(prioritization_fee_cache
609                .get_prioritization_fees(&[write_account_c])
610                .is_empty());
611            assert!(prioritization_fee_cache
612                .get_prioritization_fees(&[write_account_a, write_account_b])
613                .is_empty());
614            assert!(prioritization_fee_cache
615                .get_prioritization_fees(&[write_account_a, write_account_b, write_account_c])
616                .is_empty());
617            // after block is completed
618            sync_finalize_priority_fee_for_test(&mut prioritization_fee_cache, 1);
619            assert_eq!(
620                hashmap_of(vec![(1, 1)]),
621                prioritization_fee_cache.get_prioritization_fees(&[])
622            );
623            assert_eq!(
624                hashmap_of(vec![(1, 2)]),
625                prioritization_fee_cache.get_prioritization_fees(&[write_account_a])
626            );
627            assert_eq!(
628                hashmap_of(vec![(1, 2)]),
629                prioritization_fee_cache.get_prioritization_fees(&[write_account_b])
630            );
631            assert_eq!(
632                hashmap_of(vec![(1, 1)]),
633                prioritization_fee_cache.get_prioritization_fees(&[write_account_c])
634            );
635            assert_eq!(
636                hashmap_of(vec![(1, 2)]),
637                prioritization_fee_cache
638                    .get_prioritization_fees(&[write_account_a, write_account_b])
639            );
640            assert_eq!(
641                hashmap_of(vec![(1, 2)]),
642                prioritization_fee_cache.get_prioritization_fees(&[
643                    write_account_a,
644                    write_account_b,
645                    write_account_c
646                ])
647            );
648        }
649
650        // Assert after add one transaction for slot 2
651        {
652            let txs = vec![
653                build_sanitized_transaction_for_test(4, &write_account_b, &write_account_c),
654                build_sanitized_transaction_for_test(
655                    3,
656                    &Pubkey::new_unique(),
657                    &Pubkey::new_unique(),
658                ),
659            ];
660            sync_update(&mut prioritization_fee_cache, bank2, txs.iter());
661            // before block is marked as completed
662            assert_eq!(
663                hashmap_of(vec![(1, 1)]),
664                prioritization_fee_cache.get_prioritization_fees(&[])
665            );
666            assert_eq!(
667                hashmap_of(vec![(1, 2)]),
668                prioritization_fee_cache.get_prioritization_fees(&[write_account_a])
669            );
670            assert_eq!(
671                hashmap_of(vec![(1, 2)]),
672                prioritization_fee_cache.get_prioritization_fees(&[write_account_b])
673            );
674            assert_eq!(
675                hashmap_of(vec![(1, 1)]),
676                prioritization_fee_cache.get_prioritization_fees(&[write_account_c])
677            );
678            assert_eq!(
679                hashmap_of(vec![(1, 2)]),
680                prioritization_fee_cache
681                    .get_prioritization_fees(&[write_account_a, write_account_b])
682            );
683            assert_eq!(
684                hashmap_of(vec![(1, 2)]),
685                prioritization_fee_cache.get_prioritization_fees(&[
686                    write_account_a,
687                    write_account_b,
688                    write_account_c
689                ])
690            );
691            // after block is completed
692            sync_finalize_priority_fee_for_test(&mut prioritization_fee_cache, 2);
693            assert_eq!(
694                hashmap_of(vec![(2, 3), (1, 1)]),
695                prioritization_fee_cache.get_prioritization_fees(&[]),
696            );
697            assert_eq!(
698                hashmap_of(vec![(2, 3), (1, 2)]),
699                prioritization_fee_cache.get_prioritization_fees(&[write_account_a]),
700            );
701            assert_eq!(
702                hashmap_of(vec![(2, 4), (1, 2)]),
703                prioritization_fee_cache.get_prioritization_fees(&[write_account_b]),
704            );
705            assert_eq!(
706                hashmap_of(vec![(2, 4), (1, 1)]),
707                prioritization_fee_cache.get_prioritization_fees(&[write_account_c]),
708            );
709            assert_eq!(
710                hashmap_of(vec![(2, 4), (1, 2)]),
711                prioritization_fee_cache
712                    .get_prioritization_fees(&[write_account_a, write_account_b]),
713            );
714            assert_eq!(
715                hashmap_of(vec![(2, 4), (1, 2)]),
716                prioritization_fee_cache.get_prioritization_fees(&[
717                    write_account_a,
718                    write_account_b,
719                    write_account_c,
720                ]),
721            );
722        }
723
724        // Assert after add one transaction for slot 3
725        {
726            let txs = vec![
727                build_sanitized_transaction_for_test(6, &write_account_a, &write_account_c),
728                build_sanitized_transaction_for_test(
729                    5,
730                    &Pubkey::new_unique(),
731                    &Pubkey::new_unique(),
732                ),
733            ];
734            sync_update(&mut prioritization_fee_cache, bank3, txs.iter());
735            // before block is marked as completed
736            assert_eq!(
737                hashmap_of(vec![(2, 3), (1, 1)]),
738                prioritization_fee_cache.get_prioritization_fees(&[]),
739            );
740            assert_eq!(
741                hashmap_of(vec![(2, 3), (1, 2)]),
742                prioritization_fee_cache.get_prioritization_fees(&[write_account_a]),
743            );
744            assert_eq!(
745                hashmap_of(vec![(2, 4), (1, 2)]),
746                prioritization_fee_cache.get_prioritization_fees(&[write_account_b]),
747            );
748            assert_eq!(
749                hashmap_of(vec![(2, 4), (1, 1)]),
750                prioritization_fee_cache.get_prioritization_fees(&[write_account_c]),
751            );
752            assert_eq!(
753                hashmap_of(vec![(2, 4), (1, 2)]),
754                prioritization_fee_cache
755                    .get_prioritization_fees(&[write_account_a, write_account_b]),
756            );
757            assert_eq!(
758                hashmap_of(vec![(2, 4), (1, 2)]),
759                prioritization_fee_cache.get_prioritization_fees(&[
760                    write_account_a,
761                    write_account_b,
762                    write_account_c,
763                ]),
764            );
765            // after block is completed
766            sync_finalize_priority_fee_for_test(&mut prioritization_fee_cache, 3);
767            assert_eq!(
768                hashmap_of(vec![(3, 5), (2, 3), (1, 1)]),
769                prioritization_fee_cache.get_prioritization_fees(&[]),
770            );
771            assert_eq!(
772                hashmap_of(vec![(3, 6), (2, 3), (1, 2)]),
773                prioritization_fee_cache.get_prioritization_fees(&[write_account_a]),
774            );
775            assert_eq!(
776                hashmap_of(vec![(3, 5), (2, 4), (1, 2)]),
777                prioritization_fee_cache.get_prioritization_fees(&[write_account_b]),
778            );
779            assert_eq!(
780                hashmap_of(vec![(3, 6), (2, 4), (1, 1)]),
781                prioritization_fee_cache.get_prioritization_fees(&[write_account_c]),
782            );
783            assert_eq!(
784                hashmap_of(vec![(3, 6), (2, 4), (1, 2)]),
785                prioritization_fee_cache
786                    .get_prioritization_fees(&[write_account_a, write_account_b]),
787            );
788            assert_eq!(
789                hashmap_of(vec![(3, 6), (2, 4), (1, 2)]),
790                prioritization_fee_cache.get_prioritization_fees(&[
791                    write_account_a,
792                    write_account_b,
793                    write_account_c,
794                ]),
795            );
796        }
797    }
798}