Skip to content

Commit 1e08195

Browse files
committed
[refactor] move new tx logic to txdownload
Also delete external RecentRejectsReconsiderableFilter() access since it is no longer necessary.
1 parent 257568e commit 1e08195

File tree

4 files changed

+75
-56
lines changed

4 files changed

+75
-56
lines changed

src/net_processing.cpp

Lines changed: 10 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -808,12 +808,6 @@ class PeerManagerImpl final : public PeerManager
808808
/** Stalling timeout for blocks in IBD */
809809
std::atomic<std::chrono::seconds> m_block_stalling_timeout{BLOCK_STALLING_TIMEOUT_DEFAULT};
810810

811-
CRollingBloomFilter& RecentRejectsReconsiderableFilter() EXCLUSIVE_LOCKS_REQUIRED(m_tx_download_mutex)
812-
{
813-
AssertLockHeld(m_tx_download_mutex);
814-
return m_txdownloadman.RecentRejectsReconsiderableFilter();
815-
}
816-
817811
/**
818812
* For sending `inv`s to inbound peers, we use a single (exponentially
819813
* distributed) timer for all peers. If we used a separate timer for each
@@ -4239,24 +4233,8 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
42394233

42404234
LOCK2(cs_main, m_tx_download_mutex);
42414235

4242-
auto& m_txrequest = m_txdownloadman.GetTxRequestRef();
4243-
4244-
m_txrequest.ReceivedResponse(pfrom.GetId(), txid);
4245-
if (tx.HasWitness()) m_txrequest.ReceivedResponse(pfrom.GetId(), wtxid);
4246-
4247-
// We do the AlreadyHaveTx() check using wtxid, rather than txid - in the
4248-
// absence of witness malleation, this is strictly better, because the
4249-
// recent rejects filter may contain the wtxid but rarely contains
4250-
// the txid of a segwit transaction that has been rejected.
4251-
// In the presence of witness malleation, it's possible that by only
4252-
// doing the check with wtxid, we could overlook a transaction which
4253-
// was confirmed with a different witness, or exists in our mempool
4254-
// with a different witness, but this has limited downside:
4255-
// mempool validation does its own lookup of whether we have the txid
4256-
// already; and an adversary can already relay us old transactions
4257-
// (older than our recency filter) if trying to DoS us, without any need
4258-
// for witness malleation.
4259-
if (m_txdownloadman.AlreadyHaveTx(GenTxid::Wtxid(wtxid), /*include_reconsiderable=*/true)) {
4236+
const auto& [should_validate, package_to_validate] = m_txdownloadman.ReceivedTx(pfrom.GetId(), ptx);
4237+
if (!should_validate) {
42604238
if (pfrom.HasPermission(NetPermissionFlags::ForceRelay)) {
42614239
// Always relay transactions received from peers with forcerelay
42624240
// permission, even if they were already in the mempool, allowing
@@ -4271,37 +4249,18 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
42714249
}
42724250
}
42734251

4274-
if (RecentRejectsReconsiderableFilter().contains(wtxid)) {
4275-
// When a transaction is already in m_lazy_recent_rejects_reconsiderable, we shouldn't submit
4276-
// it by itself again. However, look for a matching child in the orphanage, as it is
4277-
// possible that they succeed as a package.
4278-
LogDebug(BCLog::TXPACKAGES, "found tx %s (wtxid=%s) in reconsiderable rejects, looking for child in orphanage\n",
4279-
txid.ToString(), wtxid.ToString());
4280-
if (auto package_to_validate{m_txdownloadman.Find1P1CPackage(ptx, pfrom.GetId())}) {
4281-
const auto package_result{ProcessNewPackage(m_chainman.ActiveChainstate(), m_mempool, package_to_validate->m_txns, /*test_accept=*/false, /*client_maxfeerate=*/std::nullopt)};
4282-
LogDebug(BCLog::TXPACKAGES, "package evaluation for %s: %s\n", package_to_validate->ToString(),
4283-
package_result.m_state.IsValid() ? "package accepted" : "package rejected");
4284-
ProcessPackageResult(package_to_validate.value(), package_result);
4285-
}
4252+
if (package_to_validate) {
4253+
const auto package_result{ProcessNewPackage(m_chainman.ActiveChainstate(), m_mempool, package_to_validate->m_txns, /*test_accept=*/false, /*client_maxfeerate=*/std::nullopt)};
4254+
LogDebug(BCLog::TXPACKAGES, "package evaluation for %s: %s\n", package_to_validate->ToString(),
4255+
package_result.m_state.IsValid() ? "package accepted" : "package rejected");
4256+
ProcessPackageResult(package_to_validate.value(), package_result);
42864257
}
4287-
// If a tx is detected by m_lazy_recent_rejects it is ignored. Because we haven't
4288-
// submitted the tx to our mempool, we won't have computed a DoS
4289-
// score for it or determined exactly why we consider it invalid.
4290-
//
4291-
// This means we won't penalize any peer subsequently relaying a DoSy
4292-
// tx (even if we penalized the first peer who gave it to us) because
4293-
// we have to account for m_lazy_recent_rejects showing false positives. In
4294-
// other words, we shouldn't penalize a peer if we aren't *sure* they
4295-
// submitted a DoSy tx.
4296-
//
4297-
// Note that m_lazy_recent_rejects doesn't just record DoSy or invalid
4298-
// transactions, but any tx not accepted by the mempool, which may be
4299-
// due to node policy (vs. consensus). So we can't blanket penalize a
4300-
// peer simply for relaying a tx that our m_lazy_recent_rejects has caught,
4301-
// regardless of false positives.
43024258
return;
43034259
}
43044260

4261+
// ReceivedTx should not be telling us to validate the tx and a package.
4262+
Assume(!package_to_validate.has_value());
4263+
43054264
const MempoolAcceptResult result = m_chainman.ProcessTransaction(ptx);
43064265
const TxValidationState& state = result.m_state;
43074266

src/node/txdownloadman.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,6 @@ class TxDownloadManager {
125125
// temporary and removed later once logic has been moved internally.
126126
TxOrphanage& GetOrphanageRef();
127127
TxRequestTracker& GetTxRequestRef();
128-
CRollingBloomFilter& RecentRejectsReconsiderableFilter();
129128

130129
// Responses to chain events. TxDownloadManager is not an actual client of ValidationInterface, these are called through PeerManager.
131130
void ActiveTipChange();
@@ -171,6 +170,11 @@ class TxDownloadManager {
171170

172171
/** Respond to package rejected from mempool */
173172
void MempoolRejectedPackage(const Package& package);
173+
174+
/** Marks a tx as ReceivedResponse in txrequest and checks whether AlreadyHaveTx.
175+
* Return a bool indicating whether this tx should be validated. If false, optionally, a
176+
* PackageToValidate. */
177+
std::pair<bool, std::optional<PackageToValidate>> ReceivedTx(NodeId nodeid, const CTransactionRef& ptx);
174178
};
175179
} // namespace node
176180
#endif // BITCOIN_NODE_TXDOWNLOADMAN_H

src/node/txdownloadman_impl.cpp

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,6 @@ TxRequestTracker& TxDownloadManager::GetTxRequestRef()
2727
{
2828
return m_impl->m_txrequest;
2929
}
30-
CRollingBloomFilter& TxDownloadManager::RecentRejectsReconsiderableFilter()
31-
{
32-
return m_impl->RecentRejectsReconsiderableFilter();
33-
}
3430
void TxDownloadManager::ActiveTipChange()
3531
{
3632
m_impl->ActiveTipChange();
@@ -83,6 +79,10 @@ void TxDownloadManager::MempoolRejectedPackage(const Package& package)
8379
{
8480
m_impl->MempoolRejectedPackage(package);
8581
}
82+
std::pair<bool, std::optional<PackageToValidate>> TxDownloadManager::ReceivedTx(NodeId nodeid, const CTransactionRef& ptx)
83+
{
84+
return m_impl->ReceivedTx(nodeid, ptx);
85+
}
8686

8787
// TxDownloadManagerImpl
8888
void TxDownloadManagerImpl::ActiveTipChange()
@@ -450,4 +450,58 @@ void TxDownloadManagerImpl::MempoolRejectedPackage(const Package& package)
450450
{
451451
RecentRejectsReconsiderableFilter().insert(GetPackageHash(package));
452452
}
453+
454+
std::pair<bool, std::optional<PackageToValidate>> TxDownloadManagerImpl::ReceivedTx(NodeId nodeid, const CTransactionRef& ptx)
455+
{
456+
const uint256& txid = ptx->GetHash();
457+
const uint256& wtxid = ptx->GetWitnessHash();
458+
459+
// Mark that we have received a response
460+
m_txrequest.ReceivedResponse(nodeid, txid);
461+
if (ptx->HasWitness()) m_txrequest.ReceivedResponse(nodeid, wtxid);
462+
463+
// First check if we should drop this tx.
464+
// We do the AlreadyHaveTx() check using wtxid, rather than txid - in the
465+
// absence of witness malleation, this is strictly better, because the
466+
// recent rejects filter may contain the wtxid but rarely contains
467+
// the txid of a segwit transaction that has been rejected.
468+
// In the presence of witness malleation, it's possible that by only
469+
// doing the check with wtxid, we could overlook a transaction which
470+
// was confirmed with a different witness, or exists in our mempool
471+
// with a different witness, but this has limited downside:
472+
// mempool validation does its own lookup of whether we have the txid
473+
// already; and an adversary can already relay us old transactions
474+
// (older than our recency filter) if trying to DoS us, without any need
475+
// for witness malleation.
476+
if (AlreadyHaveTx(GenTxid::Wtxid(wtxid), /*include_reconsiderable=*/true)) {
477+
478+
if (RecentRejectsReconsiderableFilter().contains(wtxid)) {
479+
// When a transaction is already in m_lazy_recent_rejects_reconsiderable, we shouldn't submit
480+
// it by itself again. However, look for a matching child in the orphanage, as it is
481+
// possible that they succeed as a package.
482+
LogDebug(BCLog::TXPACKAGES, "found tx %s (wtxid=%s) in reconsiderable rejects, looking for child in orphanage\n",
483+
txid.ToString(), wtxid.ToString());
484+
return std::make_pair(false, Find1P1CPackage(ptx, nodeid));
485+
}
486+
487+
// If a tx is detected by m_lazy_recent_rejects it is ignored. Because we haven't
488+
// submitted the tx to our mempool, we won't have computed a DoS
489+
// score for it or determined exactly why we consider it invalid.
490+
//
491+
// This means we won't penalize any peer subsequently relaying a DoSy
492+
// tx (even if we penalized the first peer who gave it to us) because
493+
// we have to account for m_lazy_recent_rejects showing false positives. In
494+
// other words, we shouldn't penalize a peer if we aren't *sure* they
495+
// submitted a DoSy tx.
496+
//
497+
// Note that m_lazy_recent_rejects doesn't just record DoSy or invalid
498+
// transactions, but any tx not accepted by the mempool, which may be
499+
// due to node policy (vs. consensus). So we can't blanket penalize a
500+
// peer simply for relaying a tx that our m_lazy_recent_rejects has caught,
501+
// regardless of false positives.
502+
return {false, std::nullopt};
503+
}
504+
505+
return {true, std::nullopt};
506+
}
453507
} // namespace node

src/node/txdownloadman_impl.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,8 @@ class TxDownloadManagerImpl {
167167
void MempoolAcceptedTx(const CTransactionRef& tx);
168168
RejectedTxTodo MempoolRejectedTx(const CTransactionRef& ptx, const TxValidationState& state, NodeId nodeid, bool first_time_failure);
169169
void MempoolRejectedPackage(const Package& package);
170+
171+
std::pair<bool, std::optional<PackageToValidate>> ReceivedTx(NodeId nodeid, const CTransactionRef& ptx);
170172
};
171173
} // namespace node
172174
#endif // BITCOIN_NODE_TXDOWNLOADMAN_IMPL_H

0 commit comments

Comments
 (0)