Branch data Line data Source code
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 : 1967 : TxDownloadManager::TxDownloadManager(const TxDownloadOptions& options) :
18 : 1967 : m_impl{std::make_unique<TxDownloadManagerImpl>(options)}
19 : 1967 : {}
20 : 1967 : TxDownloadManager::~TxDownloadManager() = default;
21 : :
22 : 143 : void TxDownloadManager::ActiveTipChange()
23 : : {
24 : 143 : m_impl->ActiveTipChange();
25 : 143 : }
26 : 2924 : void TxDownloadManager::BlockConnected(const std::shared_ptr<const CBlock>& pblock)
27 : : {
28 : 2924 : m_impl->BlockConnected(pblock);
29 : 2924 : }
30 : 218 : void TxDownloadManager::BlockDisconnected()
31 : : {
32 : 218 : m_impl->BlockDisconnected();
33 : 218 : }
34 : 10356 : void TxDownloadManager::ConnectedPeer(NodeId nodeid, const TxDownloadConnectionInfo& info)
35 : : {
36 : 10356 : m_impl->ConnectedPeer(nodeid, info);
37 : 10356 : }
38 : 32878 : void TxDownloadManager::DisconnectedPeer(NodeId nodeid)
39 : : {
40 : 32878 : m_impl->DisconnectedPeer(nodeid);
41 : 32878 : }
42 : 132798 : bool TxDownloadManager::AddTxAnnouncement(NodeId peer, const GenTxid& gtxid, std::chrono::microseconds now, bool p2p_inv)
43 : : {
44 : 132798 : return m_impl->AddTxAnnouncement(peer, gtxid, now, p2p_inv);
45 : : }
46 : 1022334 : std::vector<GenTxid> TxDownloadManager::GetRequestsToSend(NodeId nodeid, std::chrono::microseconds current_time)
47 : : {
48 : 1022334 : return m_impl->GetRequestsToSend(nodeid, current_time);
49 : : }
50 : 455 : void TxDownloadManager::ReceivedNotFound(NodeId nodeid, const std::vector<uint256>& txhashes)
51 : : {
52 : 455 : m_impl->ReceivedNotFound(nodeid, txhashes);
53 : 455 : }
54 : 416 : void TxDownloadManager::MempoolAcceptedTx(const CTransactionRef& tx)
55 : : {
56 : 416 : m_impl->MempoolAcceptedTx(tx);
57 : 416 : }
58 : 4554 : RejectedTxTodo TxDownloadManager::MempoolRejectedTx(const CTransactionRef& ptx, const TxValidationState& state, NodeId nodeid, bool first_time_failure)
59 : : {
60 : 4554 : return m_impl->MempoolRejectedTx(ptx, state, nodeid, first_time_failure);
61 : : }
62 : 0 : void TxDownloadManager::MempoolRejectedPackage(const Package& package)
63 : : {
64 : 0 : m_impl->MempoolRejectedPackage(package);
65 : 0 : }
66 : 16200 : std::pair<bool, std::optional<PackageToValidate>> TxDownloadManager::ReceivedTx(NodeId nodeid, const CTransactionRef& ptx)
67 : : {
68 : 16200 : return m_impl->ReceivedTx(nodeid, ptx);
69 : : }
70 : 80345 : bool TxDownloadManager::HaveMoreWork(NodeId nodeid) const
71 : : {
72 : 80345 : return m_impl->HaveMoreWork(nodeid);
73 : : }
74 : 1058790 : CTransactionRef TxDownloadManager::GetTxToReconsider(NodeId nodeid)
75 : : {
76 : 1058790 : return m_impl->GetTxToReconsider(nodeid);
77 : : }
78 : 9553 : void TxDownloadManager::CheckIsEmpty() const
79 : : {
80 : 9553 : m_impl->CheckIsEmpty();
81 : 9553 : }
82 : 32878 : void TxDownloadManager::CheckIsEmpty(NodeId nodeid) const
83 : : {
84 : 32878 : m_impl->CheckIsEmpty(nodeid);
85 : 32878 : }
86 : 5 : std::vector<TxOrphanage::OrphanTxBase> TxDownloadManager::GetOrphanTransactions() const
87 : : {
88 : 5 : return m_impl->GetOrphanTransactions();
89 : : }
90 : :
91 : : // TxDownloadManagerImpl
92 : 371 : void TxDownloadManagerImpl::ActiveTipChange()
93 : : {
94 : 371 : RecentRejectsFilter().reset();
95 : 371 : RecentRejectsReconsiderableFilter().reset();
96 : 371 : }
97 : :
98 : 5858 : void TxDownloadManagerImpl::BlockConnected(const std::shared_ptr<const CBlock>& pblock)
99 : : {
100 : 5858 : m_orphanage.EraseForBlock(*pblock);
101 : :
102 [ + + ]: 11716 : for (const auto& ptx : pblock->vtx) {
103 : 5858 : RecentConfirmedTransactionsFilter().insert(ptx->GetHash().ToUint256());
104 [ + + ]: 5858 : if (ptx->HasWitness()) {
105 : 5637 : RecentConfirmedTransactionsFilter().insert(ptx->GetWitnessHash().ToUint256());
106 : : }
107 : 5858 : m_txrequest.ForgetTxHash(ptx->GetHash());
108 : 5858 : m_txrequest.ForgetTxHash(ptx->GetWitnessHash());
109 : : }
110 : 5858 : }
111 : :
112 : 376 : 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 : 376 : RecentConfirmedTransactionsFilter().reset();
123 : 376 : }
124 : :
125 : 288929 : bool TxDownloadManagerImpl::AlreadyHaveTx(const GenTxid& gtxid, bool include_reconsiderable)
126 : : {
127 [ + + ]: 288929 : const uint256& hash = gtxid.GetHash();
128 : :
129 [ + + ]: 288929 : if (gtxid.IsWtxid()) {
130 : : // Normal query by wtxid.
131 [ + + ]: 31692 : if (m_orphanage.HaveTx(Wtxid::FromUint256(hash))) return true;
132 : : } 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 [ + + ]: 257237 : if (m_orphanage.HaveTx(Wtxid::FromUint256(hash))) return true;
145 : : }
146 : :
147 [ + + + + ]: 288755 : if (include_reconsiderable && RecentRejectsReconsiderableFilter().contains(hash)) return true;
148 : :
149 [ + + ]: 288752 : if (RecentConfirmedTransactionsFilter().contains(hash)) return true;
150 : :
151 [ + + - + ]: 288178 : return RecentRejectsFilter().contains(hash) || m_opts.m_mempool.exists(gtxid);
152 : : }
153 : :
154 : 11451 : void TxDownloadManagerImpl::ConnectedPeer(NodeId nodeid, const TxDownloadConnectionInfo& info)
155 : : {
156 : : // If already connected (shouldn't happen in practice), exit early.
157 [ + + ]: 11451 : if (m_peer_info.contains(nodeid)) return;
158 : :
159 : 11107 : m_peer_info.try_emplace(nodeid, info);
160 [ + + ]: 11107 : if (info.m_wtxid_relay) m_num_wtxid_peers += 1;
161 : : }
162 : :
163 : 53155 : void TxDownloadManagerImpl::DisconnectedPeer(NodeId nodeid)
164 : : {
165 : 53155 : m_orphanage.EraseForPeer(nodeid);
166 : 53155 : m_txrequest.DisconnectedPeer(nodeid);
167 : :
168 [ + + ]: 53155 : if (auto it = m_peer_info.find(nodeid); it != m_peer_info.end()) {
169 [ + + ]: 11107 : if (it->second.m_connection_info.m_wtxid_relay) m_num_wtxid_peers -= 1;
170 : 11107 : m_peer_info.erase(it);
171 : : }
172 : :
173 : 53155 : }
174 : :
175 : 151225 : bool TxDownloadManagerImpl::AddTxAnnouncement(NodeId peer, const GenTxid& gtxid, std::chrono::microseconds now, bool p2p_inv)
176 : : {
177 : : // If this is an inv received from a peer and we already have it, we can drop it.
178 : : // If this is a request for the parent of an orphan, we don't drop transactions that we already have. In particular,
179 : : // we *do* want to request parents that are in m_lazy_recent_rejects_reconsiderable, since they can be CPFP'd.
180 [ + + + + ]: 151225 : if (p2p_inv && AlreadyHaveTx(gtxid, /*include_reconsiderable=*/true)) return true;
181 : :
182 : 151190 : auto it = m_peer_info.find(peer);
183 [ + + ]: 151190 : if (it == m_peer_info.end()) return false;
184 [ + + ]: 148073 : const auto& info = it->second.m_connection_info;
185 [ + + + - ]: 148073 : if (!info.m_relay_permissions && m_txrequest.Count(peer) >= MAX_PEER_TX_ANNOUNCEMENTS) {
186 : : // Too many queued announcements for this peer
187 : : return false;
188 : : }
189 : : // Decide the TxRequestTracker parameters for this announcement:
190 : : // - "preferred": if fPreferredDownload is set (= outbound, or NetPermissionFlags::NoBan permission)
191 : : // - "reqtime": current time plus delays for:
192 : : // - NONPREF_PEER_TX_DELAY for announcements from non-preferred connections
193 : : // - TXID_RELAY_DELAY for txid announcements while wtxid peers are available
194 : : // - OVERLOADED_PEER_TX_DELAY for announcements from peers which have at least
195 : : // MAX_PEER_TX_REQUEST_IN_FLIGHT requests in flight (and don't have NetPermissionFlags::Relay).
196 : 148073 : auto delay{0us};
197 [ + + ]: 148073 : if (!info.m_preferred) delay += NONPREF_PEER_TX_DELAY;
198 [ + + + + ]: 148073 : if (!gtxid.IsWtxid() && m_num_wtxid_peers > 0) delay += TXID_RELAY_DELAY;
199 [ + + + + ]: 148073 : const bool overloaded = !info.m_relay_permissions && m_txrequest.CountInFlight(peer) >= MAX_PEER_TX_REQUEST_IN_FLIGHT;
200 : 615 : if (overloaded) delay += OVERLOADED_PEER_TX_DELAY;
201 : :
202 : 148073 : m_txrequest.ReceivedInv(peer, gtxid, info.m_preferred, now + delay);
203 : :
204 : 148073 : return false;
205 : : }
206 : :
207 : 1022455 : std::vector<GenTxid> TxDownloadManagerImpl::GetRequestsToSend(NodeId nodeid, std::chrono::microseconds current_time)
208 : : {
209 : 1022455 : std::vector<GenTxid> requests;
210 : 1022455 : std::vector<std::pair<NodeId, GenTxid>> expired;
211 [ + - ]: 1022455 : auto requestable = m_txrequest.GetRequestable(nodeid, current_time, &expired);
212 [ + + ]: 1043869 : for (const auto& entry : expired) {
213 [ + - - + : 21414 : LogDebug(BCLog::NET, "timeout of inflight %s %s from peer=%d\n", entry.second.IsWtxid() ? "wtx" : "tx",
- - - - -
- ]
214 : : entry.second.GetHash().ToString(), entry.first);
215 : : }
216 [ + + ]: 1132064 : for (const GenTxid& gtxid : requestable) {
217 [ + - + + ]: 109609 : if (!AlreadyHaveTx(gtxid, /*include_reconsiderable=*/false)) {
218 [ + - - + : 109605 : LogDebug(BCLog::NET, "Requesting %s %s peer=%d\n", gtxid.IsWtxid() ? "wtx" : "tx",
- - - - -
- ]
219 : : gtxid.GetHash().ToString(), nodeid);
220 [ + - ]: 109605 : requests.emplace_back(gtxid);
221 [ + - ]: 109605 : m_txrequest.RequestedTx(nodeid, gtxid.GetHash(), current_time + GETDATA_TX_INTERVAL);
222 : : } else {
223 : : // We have already seen this transaction, no need to download. This is just a belt-and-suspenders, as
224 : : // this should already be called whenever a transaction becomes AlreadyHaveTx().
225 [ + - ]: 4 : m_txrequest.ForgetTxHash(gtxid.GetHash());
226 : : }
227 : : }
228 : 1022455 : return requests;
229 : 1022455 : }
230 : :
231 : 563 : void TxDownloadManagerImpl::ReceivedNotFound(NodeId nodeid, const std::vector<uint256>& txhashes)
232 : : {
233 [ + + ]: 1858 : for (const auto& txhash : txhashes) {
234 : : // If we receive a NOTFOUND message for a tx we requested, mark the announcement for it as
235 : : // completed in TxRequestTracker.
236 : 1295 : m_txrequest.ReceivedResponse(nodeid, txhash);
237 : : }
238 : 563 : }
239 : :
240 : 207 : std::optional<PackageToValidate> TxDownloadManagerImpl::Find1P1CPackage(const CTransactionRef& ptx, NodeId nodeid)
241 : : {
242 : 207 : const auto& parent_wtxid{ptx->GetWitnessHash()};
243 : :
244 : 207 : Assume(RecentRejectsReconsiderableFilter().contains(parent_wtxid.ToUint256()));
245 : :
246 : : // Prefer children from this peer. This helps prevent censorship attempts in which an attacker
247 : : // sends lots of fake children for the parent, and we (unluckily) keep selecting the fake
248 : : // children instead of the real one provided by the honest peer.
249 : 207 : const auto cpfp_candidates_same_peer{m_orphanage.GetChildrenFromSamePeer(ptx, nodeid)};
250 : :
251 : : // These children should be sorted from newest to oldest. In the (probably uncommon) case
252 : : // of children that replace each other, this helps us accept the highest feerate (probably the
253 : : // most recent) one efficiently.
254 [ + + ]: 207 : for (const auto& child : cpfp_candidates_same_peer) {
255 [ + + + - : 3 : Package maybe_cpfp_package{ptx, child};
- - - - ]
256 [ + - + - : 2 : if (!RecentRejectsReconsiderableFilter().contains(GetPackageHash(maybe_cpfp_package)) &&
+ - + - +
- ]
257 [ + - + - ]: 1 : !RecentRejectsFilter().contains(child->GetHash().ToUint256())) {
258 [ + - ]: 1 : return PackageToValidate{ptx, child, nodeid, nodeid};
259 : : }
260 : 1 : }
261 : :
262 : : // If no suitable candidate from the same peer is found, also try children that were provided by
263 : : // a different peer. This is useful because sometimes multiple peers announce both transactions
264 : : // to us, and we happen to download them from different peers (we wouldn't have known that these
265 : : // 2 transactions are related). We still want to find 1p1c packages then.
266 : : //
267 : : // If we start tracking all announcers of orphans, we can restrict this logic to parent + child
268 : : // pairs in which both were provided by the same peer, i.e. delete this step.
269 [ + - ]: 206 : const auto cpfp_candidates_different_peer{m_orphanage.GetChildrenFromDifferentPeer(ptx, nodeid)};
270 : :
271 : : // Find the first 1p1c that hasn't already been rejected. We randomize the order to not
272 : : // create a bias that attackers can use to delay package acceptance.
273 : : //
274 : : // Create a random permutation of the indices.
275 [ + - ]: 206 : std::vector<size_t> tx_indices(cpfp_candidates_different_peer.size());
276 : 206 : std::iota(tx_indices.begin(), tx_indices.end(), 0);
277 : 206 : std::shuffle(tx_indices.begin(), tx_indices.end(), m_opts.m_rng);
278 : :
279 [ + + ]: 206 : for (const auto index : tx_indices) {
280 : : // If we already tried a package and failed for any reason, the combined hash was
281 : : // cached in m_lazy_recent_rejects_reconsiderable.
282 [ + - + - ]: 13 : const auto [child_tx, child_sender] = cpfp_candidates_different_peer.at(index);
283 [ + + + - : 39 : Package maybe_cpfp_package{ptx, child_tx};
- - - - ]
284 [ + - + - : 26 : if (!RecentRejectsReconsiderableFilter().contains(GetPackageHash(maybe_cpfp_package)) &&
+ - + - -
+ ]
285 [ + - + - ]: 13 : !RecentRejectsFilter().contains(child_tx->GetHash().ToUint256())) {
286 [ + - ]: 13 : return PackageToValidate{ptx, child_tx, nodeid, child_sender};
287 : : }
288 [ - - + - ]: 26 : }
289 : 193 : return std::nullopt;
290 [ + - + - : 249 : }
+ - + - +
- + - ]
291 : :
292 : 664 : void TxDownloadManagerImpl::MempoolAcceptedTx(const CTransactionRef& tx)
293 : : {
294 : : // As this version of the transaction was acceptable, we can forget about any requests for it.
295 : : // No-op if the tx is not in txrequest.
296 : 664 : m_txrequest.ForgetTxHash(tx->GetHash());
297 : 664 : m_txrequest.ForgetTxHash(tx->GetWitnessHash());
298 : :
299 : 664 : m_orphanage.AddChildrenToWorkSet(*tx);
300 : : // If it came from the orphanage, remove it. No-op if the tx is not in txorphanage.
301 : 664 : m_orphanage.EraseTx(tx->GetWitnessHash());
302 : 664 : }
303 : :
304 : 4867 : node::RejectedTxTodo TxDownloadManagerImpl::MempoolRejectedTx(const CTransactionRef& ptx, const TxValidationState& state, NodeId nodeid, bool first_time_failure)
305 : : {
306 [ + + ]: 4867 : const CTransaction& tx{*ptx};
307 : : // Results returned to caller
308 : : // Whether we should call AddToCompactExtraTransactions at the end
309 : 4867 : bool add_extra_compact_tx{first_time_failure};
310 : : // Hashes to pass to AddKnownTx later
311 : 4867 : std::vector<uint256> unique_parents;
312 : : // Populated if failure is reconsiderable and eligible package is found.
313 : 4867 : std::optional<node::PackageToValidate> package_to_validate;
314 : :
315 [ + + ]: 4867 : if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) {
316 : : // Only process a new orphan if this is a first time failure, as otherwise it must be either
317 : : // already in orphanage or from 1p1c processing.
318 [ + + + - : 3179 : if (first_time_failure && !RecentRejectsFilter().contains(ptx->GetWitnessHash().ToUint256())) {
+ - + + ]
319 : 3121 : bool fRejectedParents = false; // It may be the case that the orphans parents have all been rejected
320 : :
321 : : // Deduplicate parent txids, so that we don't have to loop over
322 : : // the same parent txid more than once down below.
323 [ + - ]: 3121 : unique_parents.reserve(tx.vin.size());
324 [ + + ]: 19529 : for (const CTxIn& txin : tx.vin) {
325 : : // We start with all parents, and then remove duplicates below.
326 [ + - ]: 16408 : unique_parents.push_back(txin.prevout.hash);
327 : : }
328 : 3121 : std::sort(unique_parents.begin(), unique_parents.end());
329 : 3121 : unique_parents.erase(std::unique(unique_parents.begin(), unique_parents.end()), unique_parents.end());
330 : :
331 : : // Distinguish between parents in m_lazy_recent_rejects and m_lazy_recent_rejects_reconsiderable.
332 : : // We can tolerate having up to 1 parent in m_lazy_recent_rejects_reconsiderable since we
333 : : // submit 1p1c packages. However, fail immediately if any are in m_lazy_recent_rejects.
334 : 3121 : std::optional<uint256> rejected_parent_reconsiderable;
335 [ + + ]: 19163 : for (const uint256& parent_txid : unique_parents) {
336 [ + - + - : 16043 : if (RecentRejectsFilter().contains(parent_txid)) {
+ + ]
337 : : fRejectedParents = true;
338 : : break;
339 [ + - + - : 16042 : } else if (RecentRejectsReconsiderableFilter().contains(parent_txid) &&
- + - - ]
340 [ # # ]: 0 : !m_opts.m_mempool.exists(GenTxid::Txid(parent_txid))) {
341 : : // More than 1 parent in m_lazy_recent_rejects_reconsiderable: 1p1c will not be
342 : : // sufficient to accept this package, so just give up here.
343 [ # # ]: 0 : if (rejected_parent_reconsiderable.has_value()) {
344 : : fRejectedParents = true;
345 : : break;
346 : : }
347 : 0 : rejected_parent_reconsiderable = parent_txid;
348 : : }
349 : : }
350 [ + + ]: 3121 : if (!fRejectedParents) {
351 : 3120 : const auto current_time{GetTime<std::chrono::microseconds>()};
352 : :
353 [ + + ]: 19162 : for (const uint256& parent_txid : unique_parents) {
354 : : // Here, we only have the txid (and not wtxid) of the
355 : : // inputs, so we only request in txid mode, even for
356 : : // wtxidrelay peers.
357 : : // Eventually we should replace this with an improved
358 : : // protocol for getting all unconfirmed parents.
359 [ + - ]: 16042 : const auto gtxid{GenTxid::Txid(parent_txid)};
360 : : // Exclude m_lazy_recent_rejects_reconsiderable: the missing parent may have been
361 : : // previously rejected for being too low feerate. This orphan might CPFP it.
362 [ + - + + ]: 16042 : if (!AlreadyHaveTx(gtxid, /*include_reconsiderable=*/false)) {
363 [ + - ]: 16020 : AddTxAnnouncement(nodeid, gtxid, current_time, /*p2p_inv=*/false);
364 : : }
365 : : }
366 : :
367 : : // Potentially flip add_extra_compact_tx to false if AddTx returns false because the tx was already there
368 [ + - ]: 3120 : add_extra_compact_tx &= m_orphanage.AddTx(ptx, nodeid);
369 : :
370 : : // Once added to the orphan pool, a tx is considered AlreadyHave, and we shouldn't request it anymore.
371 [ + - ]: 3120 : m_txrequest.ForgetTxHash(tx.GetHash());
372 [ + - ]: 3120 : m_txrequest.ForgetTxHash(tx.GetWitnessHash());
373 : :
374 : : // DoS prevention: do not allow m_orphanage to grow unbounded (see CVE-2012-3789)
375 [ + - ]: 3120 : m_orphanage.LimitOrphans(m_opts.m_max_orphan_txs, m_opts.m_rng);
376 : : } else {
377 [ + - ]: 1 : unique_parents.clear();
378 [ + - - + : 1 : LogDebug(BCLog::MEMPOOL, "not keeping orphan with rejected parents %s (wtxid=%s)\n",
- - - - -
- ]
379 : : tx.GetHash().ToString(),
380 : : tx.GetWitnessHash().ToString());
381 : : // We will continue to reject this tx since it has rejected
382 : : // parents so avoid re-requesting it from other peers.
383 : : // Here we add both the txid and the wtxid, as we know that
384 : : // regardless of what witness is provided, we will not accept
385 : : // this, so we don't need to allow for redownload of this txid
386 : : // from any of our non-wtxidrelay peers.
387 [ + - + - ]: 1 : RecentRejectsFilter().insert(tx.GetHash().ToUint256());
388 [ + - + - ]: 1 : RecentRejectsFilter().insert(tx.GetWitnessHash().ToUint256());
389 [ + - ]: 1 : m_txrequest.ForgetTxHash(tx.GetHash());
390 [ + - ]: 1 : m_txrequest.ForgetTxHash(tx.GetWitnessHash());
391 : : }
392 : : }
393 [ + + ]: 1688 : } else if (state.GetResult() == TxValidationResult::TX_WITNESS_STRIPPED) {
394 : : add_extra_compact_tx = false;
395 : : } else {
396 : : // We can add the wtxid of this transaction to our reject filter.
397 : : // Do not add txids of witness transactions or witness-stripped
398 : : // transactions to the filter, as they can have been malleated;
399 : : // adding such txids to the reject filter would potentially
400 : : // interfere with relay of valid transactions from peers that
401 : : // do not support wtxid-based relay. See
402 : : // https://github.com/bitcoin/bitcoin/issues/8279 for details.
403 : : // We can remove this restriction (and always add wtxids to
404 : : // the filter even for witness stripped transactions) once
405 : : // wtxid-based relay is broadly deployed.
406 : : // See also comments in https://github.com/bitcoin/bitcoin/pull/18044#discussion_r443419034
407 : : // for concerns around weakening security of unupgraded nodes
408 : : // if we start doing this too early.
409 [ + + ]: 1651 : if (state.GetResult() == TxValidationResult::TX_RECONSIDERABLE) {
410 : : // If the result is TX_RECONSIDERABLE, add it to m_lazy_recent_rejects_reconsiderable
411 : : // because we should not download or submit this transaction by itself again, but may
412 : : // submit it as part of a package later.
413 [ + - + - ]: 199 : RecentRejectsReconsiderableFilter().insert(ptx->GetWitnessHash().ToUint256());
414 : :
415 [ + + ]: 199 : if (first_time_failure) {
416 : : // When a transaction fails for TX_RECONSIDERABLE, look for a matching child in the
417 : : // orphanage, as it is possible that they succeed as a package.
418 [ + - - + : 186 : LogDebug(BCLog::TXPACKAGES, "tx %s (wtxid=%s) failed but reconsiderable, looking for child in orphanage\n",
- - - - -
- ]
419 : : ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString());
420 [ + - + + ]: 558 : package_to_validate = Find1P1CPackage(ptx, nodeid);
421 : : }
422 : : } else {
423 [ + - + - ]: 1452 : RecentRejectsFilter().insert(ptx->GetWitnessHash().ToUint256());
424 : : }
425 [ + - ]: 1651 : m_txrequest.ForgetTxHash(ptx->GetWitnessHash());
426 : : // If the transaction failed for TX_INPUTS_NOT_STANDARD,
427 : : // then we know that the witness was irrelevant to the policy
428 : : // failure, since this check depends only on the txid
429 : : // (the scriptPubKey being spent is covered by the txid).
430 : : // Add the txid to the reject filter to prevent repeated
431 : : // processing of this transaction in the event that child
432 : : // transactions are later received (resulting in
433 : : // parent-fetching by txid via the orphan-handling logic).
434 : : // We only add the txid if it differs from the wtxid, to avoid wasting entries in the
435 : : // rolling bloom filter.
436 [ + + + + ]: 1651 : if (state.GetResult() == TxValidationResult::TX_INPUTS_NOT_STANDARD && ptx->HasWitness()) {
437 [ + - + - ]: 39 : RecentRejectsFilter().insert(ptx->GetHash().ToUint256());
438 [ + - ]: 39 : m_txrequest.ForgetTxHash(ptx->GetHash());
439 : : }
440 : : }
441 : :
442 : : // If the tx failed in ProcessOrphanTx, it should be removed from the orphanage unless the
443 : : // tx was still missing inputs. If the tx was not in the orphanage, EraseTx does nothing and returns 0.
444 [ + + + - : 4867 : if (state.GetResult() != TxValidationResult::TX_MISSING_INPUTS && m_orphanage.EraseTx(ptx->GetWitnessHash()) > 0) {
+ + ]
445 [ + - - + : 3 : LogDebug(BCLog::TXPACKAGES, " removed orphan tx %s (wtxid=%s)\n", ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString());
- - - - -
- ]
446 : : }
447 : :
448 [ + + + + ]: 4881 : return RejectedTxTodo{
449 : : .m_should_add_extra_compact_tx = add_extra_compact_tx,
450 : : .m_unique_parents = std::move(unique_parents),
451 : : .m_package_to_validate = std::move(package_to_validate)
452 : 4867 : };
453 : 4867 : }
454 : :
455 : 0 : void TxDownloadManagerImpl::MempoolRejectedPackage(const Package& package)
456 : : {
457 : 0 : RecentRejectsReconsiderableFilter().insert(GetPackageHash(package));
458 : 0 : }
459 : :
460 : 22623 : std::pair<bool, std::optional<PackageToValidate>> TxDownloadManagerImpl::ReceivedTx(NodeId nodeid, const CTransactionRef& ptx)
461 : : {
462 : 22623 : const uint256& txid = ptx->GetHash();
463 : 22623 : const uint256& wtxid = ptx->GetWitnessHash();
464 : :
465 : : // Mark that we have received a response
466 : 22623 : m_txrequest.ReceivedResponse(nodeid, txid);
467 [ + + ]: 22623 : if (ptx->HasWitness()) m_txrequest.ReceivedResponse(nodeid, wtxid);
468 : :
469 : : // First check if we should drop this tx.
470 : : // We do the AlreadyHaveTx() check using wtxid, rather than txid - in the
471 : : // absence of witness malleation, this is strictly better, because the
472 : : // recent rejects filter may contain the wtxid but rarely contains
473 : : // the txid of a segwit transaction that has been rejected.
474 : : // In the presence of witness malleation, it's possible that by only
475 : : // doing the check with wtxid, we could overlook a transaction which
476 : : // was confirmed with a different witness, or exists in our mempool
477 : : // with a different witness, but this has limited downside:
478 : : // mempool validation does its own lookup of whether we have the txid
479 : : // already; and an adversary can already relay us old transactions
480 : : // (older than our recency filter) if trying to DoS us, without any need
481 : : // for witness malleation.
482 [ + + ]: 22623 : if (AlreadyHaveTx(GenTxid::Wtxid(wtxid), /*include_reconsiderable=*/false)) {
483 : : // If a tx is detected by m_lazy_recent_rejects it is ignored. Because we haven't
484 : : // submitted the tx to our mempool, we won't have computed a DoS
485 : : // score for it or determined exactly why we consider it invalid.
486 : : //
487 : : // This means we won't penalize any peer subsequently relaying a DoSy
488 : : // tx (even if we penalized the first peer who gave it to us) because
489 : : // we have to account for m_lazy_recent_rejects showing false positives. In
490 : : // other words, we shouldn't penalize a peer if we aren't *sure* they
491 : : // submitted a DoSy tx.
492 : : //
493 : : // Note that m_lazy_recent_rejects doesn't just record DoSy or invalid
494 : : // transactions, but any tx not accepted by the mempool, which may be
495 : : // due to node policy (vs. consensus). So we can't blanket penalize a
496 : : // peer simply for relaying a tx that our m_lazy_recent_rejects has caught,
497 : : // regardless of false positives.
498 : 1275 : return {false, std::nullopt};
499 [ + + ]: 21348 : } else if (RecentRejectsReconsiderableFilter().contains(wtxid)) {
500 : : // When a transaction is already in m_lazy_recent_rejects_reconsiderable, we shouldn't submit
501 : : // it by itself again. However, look for a matching child in the orphanage, as it is
502 : : // possible that they succeed as a package.
503 [ - + - - : 21 : LogDebug(BCLog::TXPACKAGES, "found tx %s (wtxid=%s) in reconsiderable rejects, looking for child in orphanage\n",
- - ]
504 : : txid.ToString(), wtxid.ToString());
505 [ - + - + ]: 42 : return {false, Find1P1CPackage(ptx, nodeid)};
506 : : }
507 : :
508 : :
509 : 21327 : return {true, std::nullopt};
510 : : }
511 : :
512 : 80612 : bool TxDownloadManagerImpl::HaveMoreWork(NodeId nodeid)
513 : : {
514 : 80612 : return m_orphanage.HaveTxToReconsider(nodeid);
515 : : }
516 : :
517 : 1059057 : CTransactionRef TxDownloadManagerImpl::GetTxToReconsider(NodeId nodeid)
518 : : {
519 : 1059057 : return m_orphanage.GetTxToReconsider(nodeid);
520 : : }
521 : :
522 : 53155 : void TxDownloadManagerImpl::CheckIsEmpty(NodeId nodeid)
523 : : {
524 [ - + ]: 53155 : assert(m_txrequest.Count(nodeid) == 0);
525 : 53155 : }
526 : 10564 : void TxDownloadManagerImpl::CheckIsEmpty()
527 : : {
528 [ - + ]: 10564 : assert(m_orphanage.Size() == 0);
529 [ - + ]: 10564 : assert(m_txrequest.Size() == 0);
530 [ - + ]: 10564 : assert(m_num_wtxid_peers == 0);
531 : 10564 : }
532 : 5 : std::vector<TxOrphanage::OrphanTxBase> TxDownloadManagerImpl::GetOrphanTransactions() const
533 : : {
534 : 5 : return m_orphanage.GetOrphanTransactions();
535 : : }
536 : : } // namespace node
|