zebra_node_services/mempool.rs
1//! The Zebra mempool.
2//!
3//! A service that manages known unmined Zcash transactions.
4
5use std::{collections::HashSet, net::SocketAddr};
6
7use tokio::sync::oneshot;
8use zebra_chain::{
9 block,
10 transaction::{self, UnminedTx, UnminedTxId, VerifiedUnminedTx},
11 transparent,
12};
13
14use crate::BoxError;
15
16mod gossip;
17mod mempool_change;
18mod service_trait;
19mod transaction_dependencies;
20
21pub use self::{
22 gossip::Gossip,
23 mempool_change::{MempoolChange, MempoolChangeKind, MempoolTxSubscriber},
24 service_trait::MempoolService,
25 transaction_dependencies::TransactionDependencies,
26};
27
28/// A mempool service request.
29///
30/// Requests can query the current set of mempool transactions,
31/// queue transactions to be downloaded and verified, or
32/// run the mempool to check for newly verified transactions.
33///
34/// Requests can't modify the mempool directly,
35/// because all mempool transactions must be verified.
36#[derive(Debug, Eq, PartialEq)]
37pub enum Request {
38 /// Query all [`UnminedTxId`]s in the mempool.
39 TransactionIds,
40
41 /// Query matching [`UnminedTx`] in the mempool,
42 /// using a unique set of [`UnminedTxId`]s.
43 TransactionsById(HashSet<UnminedTxId>),
44
45 /// Query matching [`UnminedTx`] in the mempool,
46 /// using a unique set of [`transaction::Hash`]es. Pre-V5 transactions are matched
47 /// directly; V5 transaction are matched just by the Hash, disregarding
48 /// the [`AuthDigest`](zebra_chain::transaction::AuthDigest).
49 TransactionsByMinedId(HashSet<transaction::Hash>),
50
51 /// Request a [`transparent::Output`] identified by the given [`OutPoint`](transparent::OutPoint),
52 /// waiting until it becomes available if it is unknown.
53 ///
54 /// This request is purely informational, and there are no guarantees about
55 /// whether the UTXO remains unspent or is on the best chain, or any chain.
56 /// Its purpose is to allow orphaned mempool transaction verification.
57 ///
58 /// # Correctness
59 ///
60 /// Output requests should be wrapped in a timeout, so that
61 /// out-of-order and invalid requests do not hang indefinitely.
62 ///
63 /// Outdated requests are pruned on a regular basis.
64 AwaitOutput(transparent::OutPoint),
65
66 /// Request a [`VerifiedUnminedTx`] and its dependencies by its mined id.
67 TransactionWithDepsByMinedId(transaction::Hash),
68
69 /// Get all the [`VerifiedUnminedTx`] in the mempool.
70 ///
71 /// Equivalent to `TransactionsById(TransactionIds)`,
72 /// but each transaction also includes the `miner_fee` and `legacy_sigop_count` fields.
73 //
74 // TODO: make the Transactions response return VerifiedUnminedTx,
75 // and remove the FullTransactions variant
76 FullTransactions,
77
78 /// Query matching cached rejected transaction IDs in the mempool,
79 /// using a unique set of [`UnminedTxId`]s.
80 RejectedTransactionIds(HashSet<UnminedTxId>),
81
82 /// Queue a list of gossiped transactions or transaction IDs, or
83 /// crawled transaction IDs.
84 ///
85 /// The transaction downloader checks for duplicates across IDs and transactions.
86 Queue(Vec<Gossip>),
87
88 /// Queue transaction IDs advertised by a specific peer via an `Inv`
89 /// message, tagging each one with the announcing peer so the downloader
90 /// can enforce a per-peer queue cap. See `GHSA-4fc2-h7jh-287c`.
91 QueueFromPeer {
92 /// The transaction IDs advertised by the peer.
93 txids: HashSet<UnminedTxId>,
94 /// The address of the peer that advertised them.
95 source: SocketAddr,
96 },
97
98 /// Check for newly verified transactions.
99 ///
100 /// The transaction downloader does not push transactions into the mempool.
101 /// So a task should send this request regularly (every 5-10 seconds).
102 ///
103 /// These checks also happen for other request variants,
104 /// but we can't rely on peers to send queries regularly,
105 /// and crawler queue requests depend on peer responses.
106 /// Also, crawler requests aren't frequent enough for transaction propagation.
107 ///
108 /// # Correctness
109 ///
110 /// This request is required to avoid hangs in the mempool.
111 ///
112 /// The queue checker task can't call `poll_ready` directly on the mempool
113 /// service, because the service is wrapped in a `Buffer`. Calling
114 /// `Buffer::poll_ready` reserves a buffer slot, which can cause hangs
115 /// when too many slots are reserved but unused:
116 /// <https://docs.rs/tower/0.4.10/tower/buffer/struct.Buffer.html#a-note-on-choosing-a-bound>
117 CheckForVerifiedTransactions,
118
119 /// Request summary statistics from the mempool for `getmempoolinfo`.
120 QueueStats,
121
122 /// Check whether a transparent output is spent in the mempool.
123 UnspentOutput(transparent::OutPoint),
124}
125
126/// A response to a mempool service request.
127///
128/// Responses can read the current set of mempool transactions,
129/// check the queued status of transactions to be downloaded and verified, or
130/// confirm that the mempool has been checked for newly verified transactions.
131#[derive(Debug)]
132pub enum Response {
133 /// Returns all [`UnminedTxId`]s from the mempool.
134 TransactionIds(HashSet<UnminedTxId>),
135
136 /// Returns matching [`UnminedTx`] from the mempool.
137 ///
138 /// Since the [`Request::TransactionsById`] request is unique,
139 /// the response transactions are also unique. The same applies to
140 /// [`Request::TransactionsByMinedId`] requests, since the mempool does not allow
141 /// different transactions with different mined IDs.
142 Transactions(Vec<UnminedTx>),
143
144 /// Response to [`Request::AwaitOutput`] with the transparent output
145 UnspentOutput(transparent::Output),
146
147 /// Response to [`Request::TransactionWithDepsByMinedId`].
148 TransactionWithDeps {
149 /// The queried transaction
150 transaction: VerifiedUnminedTx,
151 /// A list of dependencies of the queried transaction.
152 dependencies: HashSet<transaction::Hash>,
153 },
154
155 /// Returns all [`VerifiedUnminedTx`] in the mempool.
156 //
157 // TODO: make the Transactions response return VerifiedUnminedTx,
158 // and remove the FullTransactions variant
159 FullTransactions {
160 /// All [`VerifiedUnminedTx`]s in the mempool
161 transactions: Vec<VerifiedUnminedTx>,
162
163 /// All transaction dependencies in the mempool
164 transaction_dependencies: TransactionDependencies,
165
166 /// Last seen chain tip hash by mempool service
167 last_seen_tip_hash: zebra_chain::block::Hash,
168 },
169
170 /// Returns matching cached rejected [`UnminedTxId`]s from the mempool,
171 RejectedTransactionIds(HashSet<UnminedTxId>),
172
173 /// Returns a list of initial queue checks results and a oneshot receiver
174 /// for awaiting download and/or verification results.
175 ///
176 /// Each result matches the request at the corresponding vector index.
177 Queued(Vec<Result<oneshot::Receiver<Result<(), BoxError>>, BoxError>>),
178
179 /// Confirms that the mempool has checked for recently verified transactions.
180 CheckedForVerifiedTransactions,
181
182 /// Summary statistics for the mempool: count, total size, memory usage, and regtest info.
183 QueueStats {
184 /// Number of transactions currently in the mempool
185 size: usize,
186 /// Total size in bytes of all transactions
187 bytes: usize,
188 /// Estimated memory usage in bytes
189 usage: usize,
190 /// Whether all transactions have been fully notified (regtest only)
191 fully_notified: Option<bool>,
192 },
193
194 /// Returns whether a transparent output is created or spent in the mempool, if present.
195 TransparentOutput(Option<CreatedOrSpent>),
196}
197
198/// Indicates whether an output was created or spent by a mempool transaction.
199#[derive(Debug)]
200pub enum CreatedOrSpent {
201 /// An unspent output that was created by a transaction in the mempool and not spent by any other mempool tx.
202 Created {
203 /// The output
204 output: transparent::Output,
205 /// The version
206 tx_version: u32,
207 /// The last seen hash
208 last_seen_hash: block::Hash,
209 },
210 /// Indicates that an output was spent by a mempool transaction.
211 Spent,
212}