solana_runtime/
rent_collector.rs

1//! Bank's wrapper around `RentCollector` to allow for overriding of some
2//! `SVMRentCollector` trait methods, which are otherwise implemented on
3//! `RentCollector` directly.
4//!
5//! Agave requires submission of logs and metrics during account rent state
6//! assessment, which is not included in the `RentCollector` implementation
7//! of the `SVMRentCollector` trait. This wrapper allows all `SVMRentCollector`
8//! methods to be passed through to the underlying `RentCollector`, except for
9//! those which require additional logging and metrics.
10
11use {
12    log::*,
13    solana_account::AccountSharedData,
14    solana_clock::Epoch,
15    solana_pubkey::Pubkey,
16    solana_rent::{Rent, RentDue},
17    solana_rent_collector::{CollectedInfo, RentCollector},
18    solana_svm_rent_collector::{rent_state::RentState, svm_rent_collector::SVMRentCollector},
19    solana_transaction_context::IndexOfAccount,
20    solana_transaction_error::{TransactionError, TransactionResult as Result},
21};
22
23/// Wrapper around `RentCollector` to allow for overriding of some
24/// `SVMRentCollector` trait methods, which are otherwise implemented on
25/// `RentCollector` directly.
26///
27/// Overrides inject logging and metrics submission into the rent state
28/// assessment process.
29pub struct RentCollectorWithMetrics(RentCollector);
30
31impl RentCollectorWithMetrics {
32    pub fn new(rent_collector: RentCollector) -> Self {
33        Self(rent_collector)
34    }
35}
36
37impl SVMRentCollector for RentCollectorWithMetrics {
38    fn collect_rent(&self, address: &Pubkey, account: &mut AccountSharedData) -> CollectedInfo {
39        self.0.collect_rent(address, account)
40    }
41
42    fn get_rent(&self) -> &Rent {
43        self.0.get_rent()
44    }
45
46    fn get_rent_due(&self, lamports: u64, data_len: usize, account_rent_epoch: Epoch) -> RentDue {
47        self.0.get_rent_due(lamports, data_len, account_rent_epoch)
48    }
49
50    // Overriden to inject logging and metrics.
51    fn check_rent_state_with_account(
52        &self,
53        pre_rent_state: &RentState,
54        post_rent_state: &RentState,
55        address: &Pubkey,
56        account_state: &AccountSharedData,
57        account_index: IndexOfAccount,
58    ) -> Result<()> {
59        submit_rent_state_metrics(pre_rent_state, post_rent_state);
60        if !solana_sdk_ids::incinerator::check_id(address)
61            && !self.transition_allowed(pre_rent_state, post_rent_state)
62        {
63            debug!(
64                "Account {} not rent exempt, state {:?}",
65                address, account_state,
66            );
67            let account_index = account_index as u8;
68            Err(TransactionError::InsufficientFundsForRent { account_index })
69        } else {
70            Ok(())
71        }
72    }
73}
74
75fn submit_rent_state_metrics(pre_rent_state: &RentState, post_rent_state: &RentState) {
76    match (pre_rent_state, post_rent_state) {
77        (&RentState::Uninitialized, &RentState::RentPaying { .. }) => {
78            inc_new_counter_info!("rent_paying_err-new_account", 1);
79        }
80        (&RentState::RentPaying { .. }, &RentState::RentPaying { .. }) => {
81            inc_new_counter_info!("rent_paying_ok-legacy", 1);
82        }
83        (_, &RentState::RentPaying { .. }) => {
84            inc_new_counter_info!("rent_paying_err-other", 1);
85        }
86        _ => {}
87    }
88}