Skip to main content

zakura_node_services/
mempool.rs

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