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