/root/bitcoin/src/node/txdownloadman_impl.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) 2024 |
2 | | // Distributed under the MIT software license, see the accompanying |
3 | | // file COPYING or http://www.opensource.org/licenses/mit-license.php. |
4 | | |
5 | | #include <node/txdownloadman_impl.h> |
6 | | #include <node/txdownloadman.h> |
7 | | |
8 | | #include <chain.h> |
9 | | #include <consensus/validation.h> |
10 | | #include <logging.h> |
11 | | #include <txmempool.h> |
12 | | #include <validation.h> |
13 | | #include <validationinterface.h> |
14 | | |
15 | | namespace node { |
16 | | // TxDownloadManager wrappers |
17 | | TxDownloadManager::TxDownloadManager(const TxDownloadOptions& options) : |
18 | 0 | m_impl{std::make_unique<TxDownloadManagerImpl>(options)} |
19 | 0 | {} |
20 | 0 | TxDownloadManager::~TxDownloadManager() = default; |
21 | | |
22 | | void TxDownloadManager::ActiveTipChange() |
23 | 0 | { |
24 | 0 | m_impl->ActiveTipChange(); |
25 | 0 | } |
26 | | void TxDownloadManager::BlockConnected(const std::shared_ptr<const CBlock>& pblock) |
27 | 0 | { |
28 | 0 | m_impl->BlockConnected(pblock); |
29 | 0 | } |
30 | | void TxDownloadManager::BlockDisconnected() |
31 | 0 | { |
32 | 0 | m_impl->BlockDisconnected(); |
33 | 0 | } |
34 | | void TxDownloadManager::ConnectedPeer(NodeId nodeid, const TxDownloadConnectionInfo& info) |
35 | 0 | { |
36 | 0 | m_impl->ConnectedPeer(nodeid, info); |
37 | 0 | } |
38 | | void TxDownloadManager::DisconnectedPeer(NodeId nodeid) |
39 | 0 | { |
40 | 0 | m_impl->DisconnectedPeer(nodeid); |
41 | 0 | } |
42 | | bool TxDownloadManager::AddTxAnnouncement(NodeId peer, const GenTxid& gtxid, std::chrono::microseconds now) |
43 | 0 | { |
44 | 0 | return m_impl->AddTxAnnouncement(peer, gtxid, now); |
45 | 0 | } |
46 | | std::vector<GenTxid> TxDownloadManager::GetRequestsToSend(NodeId nodeid, std::chrono::microseconds current_time) |
47 | 0 | { |
48 | 0 | return m_impl->GetRequestsToSend(nodeid, current_time); |
49 | 0 | } |
50 | | void TxDownloadManager::ReceivedNotFound(NodeId nodeid, const std::vector<uint256>& txhashes) |
51 | 0 | { |
52 | 0 | m_impl->ReceivedNotFound(nodeid, txhashes); |
53 | 0 | } |
54 | | void TxDownloadManager::MempoolAcceptedTx(const CTransactionRef& tx) |
55 | 0 | { |
56 | 0 | m_impl->MempoolAcceptedTx(tx); |
57 | 0 | } |
58 | | RejectedTxTodo TxDownloadManager::MempoolRejectedTx(const CTransactionRef& ptx, const TxValidationState& state, NodeId nodeid, bool first_time_failure) |
59 | 0 | { |
60 | 0 | return m_impl->MempoolRejectedTx(ptx, state, nodeid, first_time_failure); |
61 | 0 | } |
62 | | void TxDownloadManager::MempoolRejectedPackage(const Package& package) |
63 | 0 | { |
64 | 0 | m_impl->MempoolRejectedPackage(package); |
65 | 0 | } |
66 | | std::pair<bool, std::optional<PackageToValidate>> TxDownloadManager::ReceivedTx(NodeId nodeid, const CTransactionRef& ptx) |
67 | 0 | { |
68 | 0 | return m_impl->ReceivedTx(nodeid, ptx); |
69 | 0 | } |
70 | | bool TxDownloadManager::HaveMoreWork(NodeId nodeid) const |
71 | 0 | { |
72 | 0 | return m_impl->HaveMoreWork(nodeid); |
73 | 0 | } |
74 | | CTransactionRef TxDownloadManager::GetTxToReconsider(NodeId nodeid) |
75 | 0 | { |
76 | 0 | return m_impl->GetTxToReconsider(nodeid); |
77 | 0 | } |
78 | | void TxDownloadManager::CheckIsEmpty() const |
79 | 0 | { |
80 | 0 | m_impl->CheckIsEmpty(); |
81 | 0 | } |
82 | | void TxDownloadManager::CheckIsEmpty(NodeId nodeid) const |
83 | 0 | { |
84 | 0 | m_impl->CheckIsEmpty(nodeid); |
85 | 0 | } |
86 | | std::vector<TxOrphanage::OrphanTxBase> TxDownloadManager::GetOrphanTransactions() const |
87 | 0 | { |
88 | 0 | return m_impl->GetOrphanTransactions(); |
89 | 0 | } |
90 | | |
91 | | // TxDownloadManagerImpl |
92 | | void TxDownloadManagerImpl::ActiveTipChange() |
93 | 0 | { |
94 | 0 | RecentRejectsFilter().reset(); |
95 | 0 | RecentRejectsReconsiderableFilter().reset(); |
96 | 0 | } |
97 | | |
98 | | void TxDownloadManagerImpl::BlockConnected(const std::shared_ptr<const CBlock>& pblock) |
99 | 0 | { |
100 | 0 | m_orphanage.EraseForBlock(*pblock); |
101 | |
|
102 | 0 | for (const auto& ptx : pblock->vtx) { |
103 | 0 | RecentConfirmedTransactionsFilter().insert(ptx->GetHash().ToUint256()); |
104 | 0 | if (ptx->HasWitness()) { |
105 | 0 | RecentConfirmedTransactionsFilter().insert(ptx->GetWitnessHash().ToUint256()); |
106 | 0 | } |
107 | 0 | m_txrequest.ForgetTxHash(ptx->GetHash()); |
108 | 0 | m_txrequest.ForgetTxHash(ptx->GetWitnessHash()); |
109 | 0 | } |
110 | 0 | } |
111 | | |
112 | | void TxDownloadManagerImpl::BlockDisconnected() |
113 | 0 | { |
114 | | // To avoid relay problems with transactions that were previously |
115 | | // confirmed, clear our filter of recently confirmed transactions whenever |
116 | | // there's a reorg. |
117 | | // This means that in a 1-block reorg (where 1 block is disconnected and |
118 | | // then another block reconnected), our filter will drop to having only one |
119 | | // block's worth of transactions in it, but that should be fine, since |
120 | | // presumably the most common case of relaying a confirmed transaction |
121 | | // should be just after a new block containing it is found. |
122 | 0 | RecentConfirmedTransactionsFilter().reset(); |
123 | 0 | } |
124 | | |
125 | | bool TxDownloadManagerImpl::AlreadyHaveTx(const GenTxid& gtxid, bool include_reconsiderable) |
126 | 0 | { |
127 | 0 | const uint256& hash = gtxid.GetHash(); |
128 | |
|
129 | 0 | if (gtxid.IsWtxid()) { |
130 | | // Normal query by wtxid. |
131 | 0 | if (m_orphanage.HaveTx(Wtxid::FromUint256(hash))) return true; |
132 | 0 | } else { |
133 | | // Never query by txid: it is possible that the transaction in the orphanage has the same |
134 | | // txid but a different witness, which would give us a false positive result. If we decided |
135 | | // not to request the transaction based on this result, an attacker could prevent us from |
136 | | // downloading a transaction by intentionally creating a malleated version of it. While |
137 | | // only one (or none!) of these transactions can ultimately be confirmed, we have no way of |
138 | | // discerning which one that is, so the orphanage can store multiple transactions with the |
139 | | // same txid. |
140 | | // |
141 | | // While we won't query by txid, we can try to "guess" what the wtxid is based on the txid. |
142 | | // A non-segwit transaction's txid == wtxid. Query this txid "casted" to a wtxid. This will |
143 | | // help us find non-segwit transactions, saving bandwidth, and should have no false positives. |
144 | 0 | if (m_orphanage.HaveTx(Wtxid::FromUint256(hash))) return true; |
145 | 0 | } |
146 | | |
147 | 0 | if (include_reconsiderable && RecentRejectsReconsiderableFilter().contains(hash)) return true; |
148 | | |
149 | 0 | if (RecentConfirmedTransactionsFilter().contains(hash)) return true; |
150 | | |
151 | 0 | return RecentRejectsFilter().contains(hash) || m_opts.m_mempool.exists(gtxid); |
152 | 0 | } |
153 | | |
154 | | void TxDownloadManagerImpl::ConnectedPeer(NodeId nodeid, const TxDownloadConnectionInfo& info) |
155 | 0 | { |
156 | | // If already connected (shouldn't happen in practice), exit early. |
157 | 0 | if (m_peer_info.contains(nodeid)) return; |
158 | | |
159 | 0 | m_peer_info.try_emplace(nodeid, info); |
160 | 0 | if (info.m_wtxid_relay) m_num_wtxid_peers += 1; |
161 | 0 | } |
162 | | |
163 | | void TxDownloadManagerImpl::DisconnectedPeer(NodeId nodeid) |
164 | 0 | { |
165 | 0 | m_orphanage.EraseForPeer(nodeid); |
166 | 0 | m_txrequest.DisconnectedPeer(nodeid); |
167 | |
|
168 | 0 | if (auto it = m_peer_info.find(nodeid); it != m_peer_info.end()) { |
169 | 0 | if (it->second.m_connection_info.m_wtxid_relay) m_num_wtxid_peers -= 1; |
170 | 0 | m_peer_info.erase(it); |
171 | 0 | } |
172 | |
|
173 | 0 | } |
174 | | |
175 | | bool TxDownloadManagerImpl::AddTxAnnouncement(NodeId peer, const GenTxid& gtxid, std::chrono::microseconds now) |
176 | 0 | { |
177 | | // If this is an orphan we are trying to resolve, consider this peer as a orphan resolution candidate instead. |
178 | | // - is wtxid matching something in orphanage |
179 | | // - exists in orphanage |
180 | | // - peer can be an orphan resolution candidate |
181 | 0 | if (gtxid.IsWtxid()) { |
182 | 0 | const auto wtxid{Wtxid::FromUint256(gtxid.GetHash())}; |
183 | 0 | if (auto orphan_tx{m_orphanage.GetTx(wtxid)}) { |
184 | 0 | auto unique_parents{GetUniqueParents(*orphan_tx)}; |
185 | 0 | std::erase_if(unique_parents, [&](const auto& txid){ |
186 | 0 | return AlreadyHaveTx(GenTxid::Txid(txid), /*include_reconsiderable=*/false); |
187 | 0 | }); |
188 | | |
189 | | // The missing parents may have all been rejected or accepted since the orphan was added to the orphanage. |
190 | | // Do not delete from the orphanage, as it may be queued for processing. |
191 | 0 | if (unique_parents.empty()) { |
192 | 0 | return true; |
193 | 0 | } |
194 | | |
195 | 0 | if (MaybeAddOrphanResolutionCandidate(unique_parents, wtxid, peer, now)) { |
196 | 0 | m_orphanage.AddAnnouncer(orphan_tx->GetWitnessHash(), peer); |
197 | 0 | } |
198 | | |
199 | | // Return even if the peer isn't an orphan resolution candidate. This would be caught by AlreadyHaveTx. |
200 | 0 | return true; |
201 | 0 | } |
202 | 0 | } |
203 | | |
204 | | // If this is an inv received from a peer and we already have it, we can drop it. |
205 | 0 | if (AlreadyHaveTx(gtxid, /*include_reconsiderable=*/true)) return true; |
206 | | |
207 | 0 | auto it = m_peer_info.find(peer); |
208 | 0 | if (it == m_peer_info.end()) return false; |
209 | 0 | const auto& info = it->second.m_connection_info; |
210 | 0 | if (!info.m_relay_permissions && m_txrequest.Count(peer) >= MAX_PEER_TX_ANNOUNCEMENTS) { |
211 | | // Too many queued announcements for this peer |
212 | 0 | return false; |
213 | 0 | } |
214 | | // Decide the TxRequestTracker parameters for this announcement: |
215 | | // - "preferred": if fPreferredDownload is set (= outbound, or NetPermissionFlags::NoBan permission) |
216 | | // - "reqtime": current time plus delays for: |
217 | | // - NONPREF_PEER_TX_DELAY for announcements from non-preferred connections |
218 | | // - TXID_RELAY_DELAY for txid announcements while wtxid peers are available |
219 | | // - OVERLOADED_PEER_TX_DELAY for announcements from peers which have at least |
220 | | // MAX_PEER_TX_REQUEST_IN_FLIGHT requests in flight (and don't have NetPermissionFlags::Relay). |
221 | 0 | auto delay{0us}; |
222 | 0 | if (!info.m_preferred) delay += NONPREF_PEER_TX_DELAY; |
223 | 0 | if (!gtxid.IsWtxid() && m_num_wtxid_peers > 0) delay += TXID_RELAY_DELAY; |
224 | 0 | const bool overloaded = !info.m_relay_permissions && m_txrequest.CountInFlight(peer) >= MAX_PEER_TX_REQUEST_IN_FLIGHT; |
225 | 0 | if (overloaded) delay += OVERLOADED_PEER_TX_DELAY; |
226 | |
|
227 | 0 | m_txrequest.ReceivedInv(peer, gtxid, info.m_preferred, now + delay); |
228 | |
|
229 | 0 | return false; |
230 | 0 | } |
231 | | |
232 | | bool TxDownloadManagerImpl::MaybeAddOrphanResolutionCandidate(const std::vector<Txid>& unique_parents, const Wtxid& wtxid, NodeId nodeid, std::chrono::microseconds now) |
233 | 0 | { |
234 | 0 | auto it_peer = m_peer_info.find(nodeid); |
235 | 0 | if (it_peer == m_peer_info.end()) return false; |
236 | 0 | if (m_orphanage.HaveTxFromPeer(wtxid, nodeid)) return false; |
237 | | |
238 | 0 | const auto& peer_entry = m_peer_info.at(nodeid); |
239 | 0 | const auto& info = peer_entry.m_connection_info; |
240 | | |
241 | | // TODO: add delays and limits based on the amount of orphan resolution we are already doing |
242 | | // with this peer, how much they are using the orphanage, etc. |
243 | 0 | if (!info.m_relay_permissions) { |
244 | | // This mirrors the delaying and dropping behavior in AddTxAnnouncement in order to preserve |
245 | | // existing behavior: drop if we are tracking too many invs for this peer already. Each |
246 | | // orphan resolution involves at least 1 transaction request which may or may not be |
247 | | // currently tracked in m_txrequest, so we include that in the count. |
248 | 0 | if (m_txrequest.Count(nodeid) + unique_parents.size() > MAX_PEER_TX_ANNOUNCEMENTS) return false; |
249 | 0 | } |
250 | | |
251 | 0 | std::chrono::seconds delay{0s}; |
252 | 0 | if (!info.m_preferred) delay += NONPREF_PEER_TX_DELAY; |
253 | | // The orphan wtxid is used, but resolution entails requesting the parents by txid. Sometimes |
254 | | // parent and child are announced and thus requested around the same time, and we happen to |
255 | | // receive child sooner. Waiting a few seconds may allow us to cancel the orphan resolution |
256 | | // request if the parent arrives in that time. |
257 | 0 | if (m_num_wtxid_peers > 0) delay += TXID_RELAY_DELAY; |
258 | 0 | const bool overloaded = !info.m_relay_permissions && m_txrequest.CountInFlight(nodeid) >= MAX_PEER_TX_REQUEST_IN_FLIGHT; |
259 | 0 | if (overloaded) delay += OVERLOADED_PEER_TX_DELAY; |
260 | | |
261 | | // Treat finding orphan resolution candidate as equivalent to the peer announcing all missing parents. |
262 | | // In the future, orphan resolution may include more explicit steps |
263 | 0 | for (const auto& parent_txid : unique_parents) { |
264 | 0 | m_txrequest.ReceivedInv(nodeid, GenTxid::Txid(parent_txid), info.m_preferred, now + delay); |
265 | 0 | } |
266 | 0 | LogDebug(BCLog::TXPACKAGES, "added peer=%d as a candidate for resolving orphan %s\n", nodeid, wtxid.ToString()); |
267 | 0 | return true; |
268 | 0 | } |
269 | | |
270 | | std::vector<GenTxid> TxDownloadManagerImpl::GetRequestsToSend(NodeId nodeid, std::chrono::microseconds current_time) |
271 | 0 | { |
272 | 0 | std::vector<GenTxid> requests; |
273 | 0 | std::vector<std::pair<NodeId, GenTxid>> expired; |
274 | 0 | auto requestable = m_txrequest.GetRequestable(nodeid, current_time, &expired); |
275 | 0 | for (const auto& entry : expired) { |
276 | 0 | LogDebug(BCLog::NET, "timeout of inflight %s %s from peer=%d\n", entry.second.IsWtxid() ? "wtx" : "tx", |
277 | 0 | entry.second.GetHash().ToString(), entry.first); |
278 | 0 | } |
279 | 0 | for (const GenTxid& gtxid : requestable) { |
280 | 0 | if (!AlreadyHaveTx(gtxid, /*include_reconsiderable=*/false)) { |
281 | 0 | LogDebug(BCLog::NET, "Requesting %s %s peer=%d\n", gtxid.IsWtxid() ? "wtx" : "tx", |
282 | 0 | gtxid.GetHash().ToString(), nodeid); |
283 | 0 | requests.emplace_back(gtxid); |
284 | 0 | m_txrequest.RequestedTx(nodeid, gtxid.GetHash(), current_time + GETDATA_TX_INTERVAL); |
285 | 0 | } else { |
286 | | // We have already seen this transaction, no need to download. This is just a belt-and-suspenders, as |
287 | | // this should already be called whenever a transaction becomes AlreadyHaveTx(). |
288 | 0 | m_txrequest.ForgetTxHash(gtxid.GetHash()); |
289 | 0 | } |
290 | 0 | } |
291 | 0 | return requests; |
292 | 0 | } |
293 | | |
294 | | void TxDownloadManagerImpl::ReceivedNotFound(NodeId nodeid, const std::vector<uint256>& txhashes) |
295 | 0 | { |
296 | 0 | for (const auto& txhash : txhashes) { |
297 | | // If we receive a NOTFOUND message for a tx we requested, mark the announcement for it as |
298 | | // completed in TxRequestTracker. |
299 | 0 | m_txrequest.ReceivedResponse(nodeid, txhash); |
300 | 0 | } |
301 | 0 | } |
302 | | |
303 | | std::optional<PackageToValidate> TxDownloadManagerImpl::Find1P1CPackage(const CTransactionRef& ptx, NodeId nodeid) |
304 | 0 | { |
305 | 0 | const auto& parent_wtxid{ptx->GetWitnessHash()}; |
306 | |
|
307 | 0 | Assume(RecentRejectsReconsiderableFilter().contains(parent_wtxid.ToUint256())); |
308 | | |
309 | | // Only consider children from this peer. This helps prevent censorship attempts in which an attacker |
310 | | // sends lots of fake children for the parent, and we (unluckily) keep selecting the fake |
311 | | // children instead of the real one provided by the honest peer. Since we track all announcers |
312 | | // of an orphan, this does not exclude parent + orphan pairs that we happened to request from |
313 | | // different peers. |
314 | 0 | const auto cpfp_candidates_same_peer{m_orphanage.GetChildrenFromSamePeer(ptx, nodeid)}; |
315 | | |
316 | | // These children should be sorted from newest to oldest. In the (probably uncommon) case |
317 | | // of children that replace each other, this helps us accept the highest feerate (probably the |
318 | | // most recent) one efficiently. |
319 | 0 | for (const auto& child : cpfp_candidates_same_peer) { |
320 | 0 | Package maybe_cpfp_package{ptx, child}; |
321 | 0 | if (!RecentRejectsReconsiderableFilter().contains(GetPackageHash(maybe_cpfp_package)) && |
322 | 0 | !RecentRejectsFilter().contains(child->GetHash().ToUint256())) { |
323 | 0 | return PackageToValidate{ptx, child, nodeid, nodeid}; |
324 | 0 | } |
325 | 0 | } |
326 | 0 | return std::nullopt; |
327 | 0 | } |
328 | | |
329 | | void TxDownloadManagerImpl::MempoolAcceptedTx(const CTransactionRef& tx) |
330 | 0 | { |
331 | | // As this version of the transaction was acceptable, we can forget about any requests for it. |
332 | | // No-op if the tx is not in txrequest. |
333 | 0 | m_txrequest.ForgetTxHash(tx->GetHash()); |
334 | 0 | m_txrequest.ForgetTxHash(tx->GetWitnessHash()); |
335 | |
|
336 | 0 | m_orphanage.AddChildrenToWorkSet(*tx, m_opts.m_rng); |
337 | | // If it came from the orphanage, remove it. No-op if the tx is not in txorphanage. |
338 | 0 | m_orphanage.EraseTx(tx->GetWitnessHash()); |
339 | 0 | } |
340 | | |
341 | | std::vector<Txid> TxDownloadManagerImpl::GetUniqueParents(const CTransaction& tx) |
342 | 0 | { |
343 | 0 | std::vector<Txid> unique_parents; |
344 | 0 | unique_parents.reserve(tx.vin.size()); |
345 | 0 | for (const CTxIn& txin : tx.vin) { |
346 | | // We start with all parents, and then remove duplicates below. |
347 | 0 | unique_parents.push_back(txin.prevout.hash); |
348 | 0 | } |
349 | |
|
350 | 0 | std::sort(unique_parents.begin(), unique_parents.end()); |
351 | 0 | unique_parents.erase(std::unique(unique_parents.begin(), unique_parents.end()), unique_parents.end()); |
352 | |
|
353 | 0 | return unique_parents; |
354 | 0 | } |
355 | | |
356 | | node::RejectedTxTodo TxDownloadManagerImpl::MempoolRejectedTx(const CTransactionRef& ptx, const TxValidationState& state, NodeId nodeid, bool first_time_failure) |
357 | 0 | { |
358 | 0 | const CTransaction& tx{*ptx}; |
359 | | // Results returned to caller |
360 | | // Whether we should call AddToCompactExtraTransactions at the end |
361 | 0 | bool add_extra_compact_tx{first_time_failure}; |
362 | | // Hashes to pass to AddKnownTx later |
363 | 0 | std::vector<Txid> unique_parents; |
364 | | // Populated if failure is reconsiderable and eligible package is found. |
365 | 0 | std::optional<node::PackageToValidate> package_to_validate; |
366 | |
|
367 | 0 | if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) { |
368 | | // Only process a new orphan if this is a first time failure, as otherwise it must be either |
369 | | // already in orphanage or from 1p1c processing. |
370 | 0 | if (first_time_failure && !RecentRejectsFilter().contains(ptx->GetWitnessHash().ToUint256())) { |
371 | 0 | bool fRejectedParents = false; // It may be the case that the orphans parents have all been rejected |
372 | | |
373 | | // Deduplicate parent txids, so that we don't have to loop over |
374 | | // the same parent txid more than once down below. |
375 | 0 | unique_parents = GetUniqueParents(tx); |
376 | | |
377 | | // Distinguish between parents in m_lazy_recent_rejects and m_lazy_recent_rejects_reconsiderable. |
378 | | // We can tolerate having up to 1 parent in m_lazy_recent_rejects_reconsiderable since we |
379 | | // submit 1p1c packages. However, fail immediately if any are in m_lazy_recent_rejects. |
380 | 0 | std::optional<uint256> rejected_parent_reconsiderable; |
381 | 0 | for (const uint256& parent_txid : unique_parents) { |
382 | 0 | if (RecentRejectsFilter().contains(parent_txid)) { |
383 | 0 | fRejectedParents = true; |
384 | 0 | break; |
385 | 0 | } else if (RecentRejectsReconsiderableFilter().contains(parent_txid) && |
386 | 0 | !m_opts.m_mempool.exists(GenTxid::Txid(parent_txid))) { |
387 | | // More than 1 parent in m_lazy_recent_rejects_reconsiderable: 1p1c will not be |
388 | | // sufficient to accept this package, so just give up here. |
389 | 0 | if (rejected_parent_reconsiderable.has_value()) { |
390 | 0 | fRejectedParents = true; |
391 | 0 | break; |
392 | 0 | } |
393 | 0 | rejected_parent_reconsiderable = parent_txid; |
394 | 0 | } |
395 | 0 | } |
396 | 0 | if (!fRejectedParents) { |
397 | | // Filter parents that we already have. |
398 | | // Exclude m_lazy_recent_rejects_reconsiderable: the missing parent may have been |
399 | | // previously rejected for being too low feerate. This orphan might CPFP it. |
400 | 0 | std::erase_if(unique_parents, [&](const auto& txid){ |
401 | 0 | return AlreadyHaveTx(GenTxid::Txid(txid), /*include_reconsiderable=*/false); |
402 | 0 | }); |
403 | 0 | const auto now{GetTime<std::chrono::microseconds>()}; |
404 | 0 | const auto& wtxid = ptx->GetWitnessHash(); |
405 | | // Potentially flip add_extra_compact_tx to false if tx is already in orphanage, which |
406 | | // means it was already added to vExtraTxnForCompact. |
407 | 0 | add_extra_compact_tx &= !m_orphanage.HaveTx(wtxid); |
408 | | |
409 | | // If there is no candidate for orphan resolution, AddTx will not be called. This means |
410 | | // that if a peer is overloading us with invs and orphans, they will eventually not be |
411 | | // able to add any more transactions to the orphanage. |
412 | | // |
413 | | // Search by txid and, if the tx has a witness, wtxid |
414 | 0 | std::vector<NodeId> orphan_resolution_candidates{nodeid}; |
415 | 0 | m_txrequest.GetCandidatePeers(ptx->GetHash().ToUint256(), orphan_resolution_candidates); |
416 | 0 | if (ptx->HasWitness()) m_txrequest.GetCandidatePeers(ptx->GetWitnessHash().ToUint256(), orphan_resolution_candidates); |
417 | |
|
418 | 0 | for (const auto& nodeid : orphan_resolution_candidates) { |
419 | 0 | if (MaybeAddOrphanResolutionCandidate(unique_parents, ptx->GetWitnessHash(), nodeid, now)) { |
420 | 0 | m_orphanage.AddTx(ptx, nodeid); |
421 | 0 | } |
422 | 0 | } |
423 | | |
424 | | // Once added to the orphan pool, a tx is considered AlreadyHave, and we shouldn't request it anymore. |
425 | 0 | m_txrequest.ForgetTxHash(tx.GetHash()); |
426 | 0 | m_txrequest.ForgetTxHash(tx.GetWitnessHash()); |
427 | | |
428 | | // DoS prevention: do not allow m_orphanage to grow unbounded (see CVE-2012-3789) |
429 | | // Note that, if the orphanage reaches capacity, it's possible that we immediately evict |
430 | | // the transaction we just added. |
431 | 0 | m_orphanage.LimitOrphans(m_opts.m_max_orphan_txs, m_opts.m_rng); |
432 | 0 | } else { |
433 | 0 | unique_parents.clear(); |
434 | 0 | LogDebug(BCLog::MEMPOOL, "not keeping orphan with rejected parents %s (wtxid=%s)\n", |
435 | 0 | tx.GetHash().ToString(), |
436 | 0 | tx.GetWitnessHash().ToString()); |
437 | | // We will continue to reject this tx since it has rejected |
438 | | // parents so avoid re-requesting it from other peers. |
439 | | // Here we add both the txid and the wtxid, as we know that |
440 | | // regardless of what witness is provided, we will not accept |
441 | | // this, so we don't need to allow for redownload of this txid |
442 | | // from any of our non-wtxidrelay peers. |
443 | 0 | RecentRejectsFilter().insert(tx.GetHash().ToUint256()); |
444 | 0 | RecentRejectsFilter().insert(tx.GetWitnessHash().ToUint256()); |
445 | 0 | m_txrequest.ForgetTxHash(tx.GetHash()); |
446 | 0 | m_txrequest.ForgetTxHash(tx.GetWitnessHash()); |
447 | 0 | } |
448 | 0 | } |
449 | 0 | } else if (state.GetResult() == TxValidationResult::TX_WITNESS_STRIPPED) { |
450 | 0 | add_extra_compact_tx = false; |
451 | 0 | } else { |
452 | | // We can add the wtxid of this transaction to our reject filter. |
453 | | // Do not add txids of witness transactions or witness-stripped |
454 | | // transactions to the filter, as they can have been malleated; |
455 | | // adding such txids to the reject filter would potentially |
456 | | // interfere with relay of valid transactions from peers that |
457 | | // do not support wtxid-based relay. See |
458 | | // https://github.com/bitcoin/bitcoin/issues/8279 for details. |
459 | | // We can remove this restriction (and always add wtxids to |
460 | | // the filter even for witness stripped transactions) once |
461 | | // wtxid-based relay is broadly deployed. |
462 | | // See also comments in https://github.com/bitcoin/bitcoin/pull/18044#discussion_r443419034 |
463 | | // for concerns around weakening security of unupgraded nodes |
464 | | // if we start doing this too early. |
465 | 0 | if (state.GetResult() == TxValidationResult::TX_RECONSIDERABLE) { |
466 | | // If the result is TX_RECONSIDERABLE, add it to m_lazy_recent_rejects_reconsiderable |
467 | | // because we should not download or submit this transaction by itself again, but may |
468 | | // submit it as part of a package later. |
469 | 0 | RecentRejectsReconsiderableFilter().insert(ptx->GetWitnessHash().ToUint256()); |
470 | |
|
471 | 0 | if (first_time_failure) { |
472 | | // When a transaction fails for TX_RECONSIDERABLE, look for a matching child in the |
473 | | // orphanage, as it is possible that they succeed as a package. |
474 | 0 | LogDebug(BCLog::TXPACKAGES, "tx %s (wtxid=%s) failed but reconsiderable, looking for child in orphanage\n", |
475 | 0 | ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString()); |
476 | 0 | package_to_validate = Find1P1CPackage(ptx, nodeid); |
477 | 0 | } |
478 | 0 | } else { |
479 | 0 | RecentRejectsFilter().insert(ptx->GetWitnessHash().ToUint256()); |
480 | 0 | } |
481 | 0 | m_txrequest.ForgetTxHash(ptx->GetWitnessHash()); |
482 | | // If the transaction failed for TX_INPUTS_NOT_STANDARD, |
483 | | // then we know that the witness was irrelevant to the policy |
484 | | // failure, since this check depends only on the txid |
485 | | // (the scriptPubKey being spent is covered by the txid). |
486 | | // Add the txid to the reject filter to prevent repeated |
487 | | // processing of this transaction in the event that child |
488 | | // transactions are later received (resulting in |
489 | | // parent-fetching by txid via the orphan-handling logic). |
490 | | // We only add the txid if it differs from the wtxid, to avoid wasting entries in the |
491 | | // rolling bloom filter. |
492 | 0 | if (state.GetResult() == TxValidationResult::TX_INPUTS_NOT_STANDARD && ptx->HasWitness()) { |
493 | 0 | RecentRejectsFilter().insert(ptx->GetHash().ToUint256()); |
494 | 0 | m_txrequest.ForgetTxHash(ptx->GetHash()); |
495 | 0 | } |
496 | 0 | } |
497 | | |
498 | | // If the tx failed in ProcessOrphanTx, it should be removed from the orphanage unless the |
499 | | // tx was still missing inputs. If the tx was not in the orphanage, EraseTx does nothing and returns 0. |
500 | 0 | if (state.GetResult() != TxValidationResult::TX_MISSING_INPUTS && m_orphanage.EraseTx(ptx->GetWitnessHash()) > 0) { |
501 | 0 | LogDebug(BCLog::TXPACKAGES, " removed orphan tx %s (wtxid=%s)\n", ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString()); |
502 | 0 | } |
503 | |
|
504 | 0 | return RejectedTxTodo{ |
505 | 0 | .m_should_add_extra_compact_tx = add_extra_compact_tx, |
506 | 0 | .m_unique_parents = std::move(unique_parents), |
507 | 0 | .m_package_to_validate = std::move(package_to_validate) |
508 | 0 | }; |
509 | 0 | } |
510 | | |
511 | | void TxDownloadManagerImpl::MempoolRejectedPackage(const Package& package) |
512 | 0 | { |
513 | 0 | RecentRejectsReconsiderableFilter().insert(GetPackageHash(package)); |
514 | 0 | } |
515 | | |
516 | | std::pair<bool, std::optional<PackageToValidate>> TxDownloadManagerImpl::ReceivedTx(NodeId nodeid, const CTransactionRef& ptx) |
517 | 0 | { |
518 | 0 | const uint256& txid = ptx->GetHash(); |
519 | 0 | const uint256& wtxid = ptx->GetWitnessHash(); |
520 | | |
521 | | // Mark that we have received a response |
522 | 0 | m_txrequest.ReceivedResponse(nodeid, txid); |
523 | 0 | if (ptx->HasWitness()) m_txrequest.ReceivedResponse(nodeid, wtxid); |
524 | | |
525 | | // First check if we should drop this tx. |
526 | | // We do the AlreadyHaveTx() check using wtxid, rather than txid - in the |
527 | | // absence of witness malleation, this is strictly better, because the |
528 | | // recent rejects filter may contain the wtxid but rarely contains |
529 | | // the txid of a segwit transaction that has been rejected. |
530 | | // In the presence of witness malleation, it's possible that by only |
531 | | // doing the check with wtxid, we could overlook a transaction which |
532 | | // was confirmed with a different witness, or exists in our mempool |
533 | | // with a different witness, but this has limited downside: |
534 | | // mempool validation does its own lookup of whether we have the txid |
535 | | // already; and an adversary can already relay us old transactions |
536 | | // (older than our recency filter) if trying to DoS us, without any need |
537 | | // for witness malleation. |
538 | 0 | if (AlreadyHaveTx(GenTxid::Wtxid(wtxid), /*include_reconsiderable=*/false)) { |
539 | | // If a tx is detected by m_lazy_recent_rejects it is ignored. Because we haven't |
540 | | // submitted the tx to our mempool, we won't have computed a DoS |
541 | | // score for it or determined exactly why we consider it invalid. |
542 | | // |
543 | | // This means we won't penalize any peer subsequently relaying a DoSy |
544 | | // tx (even if we penalized the first peer who gave it to us) because |
545 | | // we have to account for m_lazy_recent_rejects showing false positives. In |
546 | | // other words, we shouldn't penalize a peer if we aren't *sure* they |
547 | | // submitted a DoSy tx. |
548 | | // |
549 | | // Note that m_lazy_recent_rejects doesn't just record DoSy or invalid |
550 | | // transactions, but any tx not accepted by the mempool, which may be |
551 | | // due to node policy (vs. consensus). So we can't blanket penalize a |
552 | | // peer simply for relaying a tx that our m_lazy_recent_rejects has caught, |
553 | | // regardless of false positives. |
554 | 0 | return {false, std::nullopt}; |
555 | 0 | } else if (RecentRejectsReconsiderableFilter().contains(wtxid)) { |
556 | | // When a transaction is already in m_lazy_recent_rejects_reconsiderable, we shouldn't submit |
557 | | // it by itself again. However, look for a matching child in the orphanage, as it is |
558 | | // possible that they succeed as a package. |
559 | 0 | LogDebug(BCLog::TXPACKAGES, "found tx %s (wtxid=%s) in reconsiderable rejects, looking for child in orphanage\n", |
560 | 0 | txid.ToString(), wtxid.ToString()); |
561 | 0 | return {false, Find1P1CPackage(ptx, nodeid)}; |
562 | 0 | } |
563 | | |
564 | | |
565 | 0 | return {true, std::nullopt}; |
566 | 0 | } |
567 | | |
568 | | bool TxDownloadManagerImpl::HaveMoreWork(NodeId nodeid) |
569 | 0 | { |
570 | 0 | return m_orphanage.HaveTxToReconsider(nodeid); |
571 | 0 | } |
572 | | |
573 | | CTransactionRef TxDownloadManagerImpl::GetTxToReconsider(NodeId nodeid) |
574 | 0 | { |
575 | 0 | return m_orphanage.GetTxToReconsider(nodeid); |
576 | 0 | } |
577 | | |
578 | | void TxDownloadManagerImpl::CheckIsEmpty(NodeId nodeid) |
579 | 0 | { |
580 | 0 | assert(m_txrequest.Count(nodeid) == 0); |
581 | 0 | assert(m_orphanage.UsageByPeer(nodeid) == 0); |
582 | 0 | } |
583 | | void TxDownloadManagerImpl::CheckIsEmpty() |
584 | 0 | { |
585 | 0 | assert(m_orphanage.TotalOrphanUsage() == 0); |
586 | 0 | assert(m_orphanage.Size() == 0); |
587 | 0 | assert(m_txrequest.Size() == 0); |
588 | 0 | assert(m_num_wtxid_peers == 0); |
589 | 0 | } |
590 | | std::vector<TxOrphanage::OrphanTxBase> TxDownloadManagerImpl::GetOrphanTransactions() const |
591 | 0 | { |
592 | 0 | return m_orphanage.GetOrphanTransactions(); |
593 | 0 | } |
594 | | } // namespace node |