kaspa_mining/mempool/
mod.rs

1use crate::{
2    feerate::{FeerateEstimator, FeerateEstimatorArgs},
3    model::{
4        owner_txs::{GroupedOwnerTransactions, ScriptPublicKeySet},
5        tx_query::TransactionQuery,
6    },
7    MiningCounters,
8};
9
10use self::{
11    config::Config,
12    model::{accepted_transactions::AcceptedTransactions, orphan_pool::OrphanPool, pool::Pool, transactions_pool::TransactionsPool},
13    tx::Priority,
14};
15use kaspa_consensus_core::{
16    block::TemplateTransactionSelector,
17    tx::{MutableTransaction, TransactionId},
18};
19use kaspa_core::time::Stopwatch;
20use std::sync::Arc;
21
22pub(crate) mod check_transaction_standard;
23pub mod config;
24pub mod errors;
25pub(crate) mod handle_new_block_transactions;
26pub(crate) mod model;
27pub(crate) mod populate_entries_and_try_validate;
28pub(crate) mod remove_transaction;
29pub(crate) mod replace_by_fee;
30pub(crate) mod validate_and_insert_transaction;
31
32/// Mempool contains transactions intended to be inserted into a block and mined.
33///
34/// Some important properties to consider:
35///
36/// - Transactions can be chained, so a transaction can have parents and chained
37///   dependencies in the mempool.
38/// - A transaction can have some of its outpoints refer to missing outputs when
39///   added to the mempool. In this case it is considered orphan.
40/// - An orphan transaction is unorphaned when all its UTXO entries have been
41///   built or found.
42/// - There are transaction priorities: high and low.
43/// - Transactions submitted to the mempool by a RPC call have **high priority**.
44///   They are owned by the node, they never expire in the mempool and the node
45///   rebroadcasts them once in a while.
46/// - Transactions received through P2P have **low-priority**. They expire after
47///   60 seconds and are removed if not inserted in a block for mining.
48pub(crate) struct Mempool {
49    config: Arc<Config>,
50    transaction_pool: TransactionsPool,
51    orphan_pool: OrphanPool,
52    accepted_transactions: AcceptedTransactions,
53    counters: Arc<MiningCounters>,
54}
55
56impl Mempool {
57    pub(crate) fn new(config: Arc<Config>, counters: Arc<MiningCounters>) -> Self {
58        let transaction_pool = TransactionsPool::new(config.clone());
59        let orphan_pool = OrphanPool::new(config.clone());
60        let accepted_transactions = AcceptedTransactions::new(config.clone());
61        Self { config, transaction_pool, orphan_pool, accepted_transactions, counters }
62    }
63
64    pub(crate) fn get_transaction(&self, transaction_id: &TransactionId, query: TransactionQuery) -> Option<MutableTransaction> {
65        let mut transaction = None;
66        if query.include_transaction_pool() {
67            transaction = self.transaction_pool.get(transaction_id);
68        }
69        if transaction.is_none() && query.include_orphan_pool() {
70            transaction = self.orphan_pool.get(transaction_id);
71        }
72        transaction.map(|x| x.mtx.clone())
73    }
74
75    pub(crate) fn has_transaction(&self, transaction_id: &TransactionId, query: TransactionQuery) -> bool {
76        (query.include_transaction_pool() && self.transaction_pool.has(transaction_id))
77            || (query.include_orphan_pool() && self.orphan_pool.has(transaction_id))
78    }
79
80    pub(crate) fn get_all_transactions(&self, query: TransactionQuery) -> (Vec<MutableTransaction>, Vec<MutableTransaction>) {
81        let transactions = if query.include_transaction_pool() { self.transaction_pool.get_all_transactions() } else { vec![] };
82        let orphans = if query.include_orphan_pool() { self.orphan_pool.get_all_transactions() } else { vec![] };
83        (transactions, orphans)
84    }
85
86    pub(crate) fn get_all_transaction_ids(&self, query: TransactionQuery) -> (Vec<TransactionId>, Vec<TransactionId>) {
87        let transactions = if query.include_transaction_pool() { self.transaction_pool.get_all_transaction_ids() } else { vec![] };
88        let orphans = if query.include_orphan_pool() { self.orphan_pool.get_all_transaction_ids() } else { vec![] };
89        (transactions, orphans)
90    }
91
92    pub(crate) fn get_transactions_by_addresses(
93        &self,
94        script_public_keys: &ScriptPublicKeySet,
95        query: TransactionQuery,
96    ) -> GroupedOwnerTransactions {
97        let mut owner_set = GroupedOwnerTransactions::default();
98        if query.include_transaction_pool() {
99            self.transaction_pool.fill_owner_set_transactions(script_public_keys, &mut owner_set);
100        }
101        if query.include_orphan_pool() {
102            self.orphan_pool.fill_owner_set_transactions(script_public_keys, &mut owner_set);
103        }
104        owner_set
105    }
106
107    pub(crate) fn transaction_count(&self, query: TransactionQuery) -> usize {
108        let mut count = 0;
109        if query.include_transaction_pool() {
110            count += self.transaction_pool.len()
111        }
112        if query.include_orphan_pool() {
113            count += self.orphan_pool.len()
114        }
115        count
116    }
117
118    pub(crate) fn ready_transaction_count(&self) -> usize {
119        self.transaction_pool.ready_transaction_count()
120    }
121
122    pub(crate) fn ready_transaction_total_mass(&self) -> u64 {
123        self.transaction_pool.ready_transaction_total_mass()
124    }
125
126    /// Dynamically builds a transaction selector based on the specific state of the ready transactions frontier
127    pub(crate) fn build_selector(&self) -> Box<dyn TemplateTransactionSelector> {
128        let _sw = Stopwatch::<10>::with_threshold("build_selector op");
129        self.transaction_pool.build_selector()
130    }
131
132    /// Builds a feerate estimator based on internal state of the ready transactions frontier
133    pub(crate) fn build_feerate_estimator(&self, args: FeerateEstimatorArgs) -> FeerateEstimator {
134        self.transaction_pool.build_feerate_estimator(args)
135    }
136
137    pub(crate) fn all_transaction_ids_with_priority(&self, priority: Priority) -> Vec<TransactionId> {
138        let _sw = Stopwatch::<15>::with_threshold("all_transaction_ids_with_priority op");
139        self.transaction_pool.all_transaction_ids_with_priority(priority)
140    }
141
142    pub(crate) fn update_revalidated_transaction(&mut self, transaction: MutableTransaction) -> bool {
143        self.transaction_pool.update_revalidated_transaction(transaction)
144    }
145
146    pub(crate) fn has_accepted_transaction(&self, transaction_id: &TransactionId) -> bool {
147        self.accepted_transactions.has(transaction_id)
148    }
149
150    pub(crate) fn unaccepted_transactions(&self, transactions: Vec<TransactionId>) -> Vec<TransactionId> {
151        self.accepted_transactions.unaccepted(&mut transactions.into_iter())
152    }
153
154    pub(crate) fn unknown_transactions(&self, transactions: Vec<TransactionId>) -> Vec<TransactionId> {
155        let mut not_in_pools_txs = transactions
156            .into_iter()
157            .filter(|transaction_id| !(self.transaction_pool.has(transaction_id) || self.orphan_pool.has(transaction_id)));
158        self.accepted_transactions.unaccepted(&mut not_in_pools_txs)
159    }
160
161    #[cfg(test)]
162    pub(crate) fn get_estimated_size(&self) -> usize {
163        self.transaction_pool.get_estimated_size()
164    }
165}
166
167pub mod tx {
168    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
169    pub enum Priority {
170        Low,
171        High,
172    }
173
174    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
175    pub enum Orphan {
176        Forbidden,
177        Allowed,
178    }
179
180    /// Replace by Fee (RBF) policy
181    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
182    pub enum RbfPolicy {
183        /// ### RBF is forbidden
184        ///
185        /// Inserts the incoming transaction.
186        ///
187        /// Conditions of success:
188        ///
189        /// - no double spend
190        ///
191        /// If conditions are not met, leaves the mempool unchanged and fails with a double spend error.
192        Forbidden,
193
194        /// ### RBF may occur
195        ///
196        /// Identifies double spends in mempool and their owning transactions checking in order every input of the incoming
197        /// transaction.
198        ///
199        /// Removes all mempool transactions owning double spends and inserts the incoming transaction.
200        ///
201        /// Conditions of success:
202        ///
203        /// - on absence of double spends, always succeeds
204        /// - on double spends, the incoming transaction has a higher fee/mass ratio than the mempool transaction owning
205        ///   the first double spend
206        ///
207        /// If conditions are not met, leaves the mempool unchanged and fails with a double spend or a tx fee/mass too low error.
208        Allowed,
209
210        /// ### RBF must occur
211        ///
212        /// Identifies double spends in mempool and their owning transactions checking in order every input of the incoming
213        /// transaction.
214        ///
215        /// Removes the mempool transaction owning the double spends and inserts the incoming transaction.
216        ///
217        /// Conditions of success:
218        ///
219        /// - at least one double spend
220        /// - all double spends belong to the same mempool transaction
221        /// - the incoming transaction has a higher fee/mass ratio than the mempool double spending transaction.
222        ///
223        /// If conditions are not met, leaves the mempool unchanged and fails with a double spend or a tx fee/mass too low error.
224        Mandatory,
225    }
226}