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