solana_accountsdb_plugin_postgres/
transaction_selector.rs

1/// The transaction selector is responsible for filtering transactions
2/// in the plugin framework.
3use {log::*, solana_sdk::pubkey::Pubkey, std::collections::HashSet};
4
5pub(crate) struct TransactionSelector {
6    pub mentioned_addresses: HashSet<Vec<u8>>,
7    pub select_all_transactions: bool,
8    pub select_all_vote_transactions: bool,
9}
10
11#[allow(dead_code)]
12impl TransactionSelector {
13    pub fn default() -> Self {
14        Self {
15            mentioned_addresses: HashSet::default(),
16            select_all_transactions: false,
17            select_all_vote_transactions: false,
18        }
19    }
20
21    /// Create a selector based on the mentioned addresses
22    /// To select all transactions use ["*"] or ["all"]
23    /// To select all vote transactions, use ["all_votes"]
24    /// To select transactions mentioning specific addresses use ["<pubkey1>", "<pubkey2>", ...]
25    pub fn new(mentioned_addresses: &[String]) -> Self {
26        info!(
27            "Creating TransactionSelector from addresses: {:?}",
28            mentioned_addresses
29        );
30
31        let select_all_transactions = mentioned_addresses
32            .iter()
33            .any(|key| key == "*" || key == "all");
34        if select_all_transactions {
35            return Self {
36                mentioned_addresses: HashSet::default(),
37                select_all_transactions,
38                select_all_vote_transactions: true,
39            };
40        }
41        let select_all_vote_transactions = mentioned_addresses.iter().any(|key| key == "all_votes");
42        if select_all_vote_transactions {
43            return Self {
44                mentioned_addresses: HashSet::default(),
45                select_all_transactions,
46                select_all_vote_transactions: true,
47            };
48        }
49
50        let mentioned_addresses = mentioned_addresses
51            .iter()
52            .map(|key| bs58::decode(key).into_vec().unwrap())
53            .collect();
54
55        Self {
56            mentioned_addresses,
57            select_all_transactions: false,
58            select_all_vote_transactions: false,
59        }
60    }
61
62    /// Check if a transaction is of interest.
63    pub fn is_transaction_selected(
64        &self,
65        is_vote: bool,
66        mentioned_addresses: Box<dyn Iterator<Item = &Pubkey> + '_>,
67    ) -> bool {
68        if !self.is_enabled() {
69            return false;
70        }
71
72        if self.select_all_transactions || (self.select_all_vote_transactions && is_vote) {
73            return true;
74        }
75        for address in mentioned_addresses {
76            if self.mentioned_addresses.contains(address.as_ref()) {
77                return true;
78            }
79        }
80        false
81    }
82
83    /// Check if any transaction is of interest at all
84    pub fn is_enabled(&self) -> bool {
85        self.select_all_transactions
86            || self.select_all_vote_transactions
87            || !self.mentioned_addresses.is_empty()
88    }
89}
90
91#[cfg(test)]
92pub(crate) mod tests {
93    use super::*;
94
95    #[test]
96    fn test_select_transaction() {
97        let pubkey1 = Pubkey::new_unique();
98        let pubkey2 = Pubkey::new_unique();
99
100        let selector = TransactionSelector::new(&[pubkey1.to_string()]);
101
102        assert!(selector.is_enabled());
103
104        let addresses = [pubkey1];
105
106        assert!(selector.is_transaction_selected(false, Box::new(addresses.iter())));
107
108        let addresses = [pubkey2];
109        assert!(!selector.is_transaction_selected(false, Box::new(addresses.iter())));
110
111        let addresses = [pubkey1, pubkey2];
112        assert!(selector.is_transaction_selected(false, Box::new(addresses.iter())));
113    }
114
115    #[test]
116    fn test_select_all_transaction_using_wildcard() {
117        let pubkey1 = Pubkey::new_unique();
118        let pubkey2 = Pubkey::new_unique();
119
120        let selector = TransactionSelector::new(&["*".to_string()]);
121
122        assert!(selector.is_enabled());
123
124        let addresses = [pubkey1];
125
126        assert!(selector.is_transaction_selected(false, Box::new(addresses.iter())));
127
128        let addresses = [pubkey2];
129        assert!(selector.is_transaction_selected(false, Box::new(addresses.iter())));
130
131        let addresses = [pubkey1, pubkey2];
132        assert!(selector.is_transaction_selected(false, Box::new(addresses.iter())));
133    }
134
135    #[test]
136    fn test_select_all_transaction_all() {
137        let pubkey1 = Pubkey::new_unique();
138        let pubkey2 = Pubkey::new_unique();
139
140        let selector = TransactionSelector::new(&["all".to_string()]);
141
142        assert!(selector.is_enabled());
143
144        let addresses = [pubkey1];
145
146        assert!(selector.is_transaction_selected(false, Box::new(addresses.iter())));
147
148        let addresses = [pubkey2];
149        assert!(selector.is_transaction_selected(false, Box::new(addresses.iter())));
150
151        let addresses = [pubkey1, pubkey2];
152        assert!(selector.is_transaction_selected(false, Box::new(addresses.iter())));
153    }
154
155    #[test]
156    fn test_select_all_vote_transaction() {
157        let pubkey1 = Pubkey::new_unique();
158        let pubkey2 = Pubkey::new_unique();
159
160        let selector = TransactionSelector::new(&["all_votes".to_string()]);
161
162        assert!(selector.is_enabled());
163
164        let addresses = [pubkey1];
165
166        assert!(!selector.is_transaction_selected(false, Box::new(addresses.iter())));
167
168        let addresses = [pubkey2];
169        assert!(selector.is_transaction_selected(true, Box::new(addresses.iter())));
170
171        let addresses = [pubkey1, pubkey2];
172        assert!(selector.is_transaction_selected(true, Box::new(addresses.iter())));
173    }
174
175    #[test]
176    fn test_select_no_transaction() {
177        let pubkey1 = Pubkey::new_unique();
178        let pubkey2 = Pubkey::new_unique();
179
180        let selector = TransactionSelector::new(&[]);
181
182        assert!(!selector.is_enabled());
183
184        let addresses = [pubkey1];
185
186        assert!(!selector.is_transaction_selected(false, Box::new(addresses.iter())));
187
188        let addresses = [pubkey2];
189        assert!(!selector.is_transaction_selected(true, Box::new(addresses.iter())));
190
191        let addresses = [pubkey1, pubkey2];
192        assert!(!selector.is_transaction_selected(true, Box::new(addresses.iter())));
193    }
194}