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