Skip to content

Commit 35477e9

Browse files
author
MarcoFalke
committed
Merge #15644: Make orphan processing interruptible
866c805 Interrupt orphan processing after every transaction (Pieter Wuille) 6e051f3 [MOVEONLY] Move processing of orphan queue to ProcessOrphanTx (Pieter Wuille) 9453018 Simplify orphan processing in preparation for interruptibility (Pieter Wuille) Pull request description: As individual orphan transactions can be relatively expensive to handle, it's undesirable to process all of them (max 100) as soon as the parent becomes available, as it pegs the net processing the whole time. Change this by interrupting orphan handling after every transactions, and continue in the next processing slot of the peer that gave us the parent - similar to how getdata processing works now. Messages from other peers arriving in the mean time are processed normally, but other messages from the peer that gave us the parent have to wait until all orphan processing is done. ACKs for commit 866c80: sdaftuar: ACK 866c805 MarcoFalke: utACK 866c805 promag: utACK 866c805. Verified refactor in 9453018 and moved code in 6e051f3. Not so sure about change in 866c805 just because I'm not familiar with net processing. Tree-SHA512: d8e8a1ee5f2999446cdeb8fc9756ed9c24f3d5cd769a7774ec4c317fc8d463fdfceec88de97266f389b715a5dfcc2b0a3abaa573955ea451786cc43b870e8cde
2 parents 79c345a + 866c805 commit 35477e9

File tree

2 files changed

+80
-62
lines changed

2 files changed

+80
-62
lines changed

src/net.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -739,6 +739,8 @@ class CNode
739739
CAmount lastSentFeeFilter{0};
740740
int64_t nextSendTimeFeeFilter{0};
741741

742+
std::set<uint256> orphan_work_set;
743+
742744
CNode(NodeId id, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress &addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const CAddress &addrBindIn, const std::string &addrNameIn = "", bool fInboundIn = false);
743745
~CNode();
744746
CNode(const CNode&) = delete;

src/net_processing.cpp

Lines changed: 78 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1713,6 +1713,67 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::ve
17131713
return true;
17141714
}
17151715

1716+
void static ProcessOrphanTx(CConnman* connman, std::set<uint256>& orphan_work_set, std::list<CTransactionRef>& removed_txn) EXCLUSIVE_LOCKS_REQUIRED(cs_main, g_cs_orphans)
1717+
{
1718+
AssertLockHeld(cs_main);
1719+
AssertLockHeld(g_cs_orphans);
1720+
std::set<NodeId> setMisbehaving;
1721+
bool done = false;
1722+
while (!done && !orphan_work_set.empty()) {
1723+
const uint256 orphanHash = *orphan_work_set.begin();
1724+
orphan_work_set.erase(orphan_work_set.begin());
1725+
1726+
auto orphan_it = mapOrphanTransactions.find(orphanHash);
1727+
if (orphan_it == mapOrphanTransactions.end()) continue;
1728+
1729+
const CTransactionRef porphanTx = orphan_it->second.tx;
1730+
const CTransaction& orphanTx = *porphanTx;
1731+
NodeId fromPeer = orphan_it->second.fromPeer;
1732+
bool fMissingInputs2 = false;
1733+
// Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan
1734+
// resolution (that is, feeding people an invalid transaction based on LegitTxX in order to get
1735+
// anyone relaying LegitTxX banned)
1736+
CValidationState stateDummy;
1737+
1738+
if (setMisbehaving.count(fromPeer)) continue;
1739+
if (AcceptToMemoryPool(mempool, stateDummy, porphanTx, &fMissingInputs2, &removed_txn, false /* bypass_limits */, 0 /* nAbsurdFee */)) {
1740+
LogPrint(BCLog::MEMPOOL, " accepted orphan tx %s\n", orphanHash.ToString());
1741+
RelayTransaction(orphanTx, connman);
1742+
for (unsigned int i = 0; i < orphanTx.vout.size(); i++) {
1743+
auto it_by_prev = mapOrphanTransactionsByPrev.find(COutPoint(orphanHash, i));
1744+
if (it_by_prev != mapOrphanTransactionsByPrev.end()) {
1745+
for (const auto& elem : it_by_prev->second) {
1746+
orphan_work_set.insert(elem->first);
1747+
}
1748+
}
1749+
}
1750+
EraseOrphanTx(orphanHash);
1751+
done = true;
1752+
} else if (!fMissingInputs2) {
1753+
int nDos = 0;
1754+
if (stateDummy.IsInvalid(nDos) && nDos > 0) {
1755+
// Punish peer that gave us an invalid orphan tx
1756+
Misbehaving(fromPeer, nDos);
1757+
setMisbehaving.insert(fromPeer);
1758+
LogPrint(BCLog::MEMPOOL, " invalid orphan tx %s\n", orphanHash.ToString());
1759+
}
1760+
// Has inputs but not accepted to mempool
1761+
// Probably non-standard or insufficient fee
1762+
LogPrint(BCLog::MEMPOOL, " removed orphan tx %s\n", orphanHash.ToString());
1763+
if (!orphanTx.HasWitness() && !stateDummy.CorruptionPossible()) {
1764+
// Do not use rejection cache for witness transactions or
1765+
// witness-stripped transactions, as they can have been malleated.
1766+
// See https://github.com/bitcoin/bitcoin/issues/8279 for details.
1767+
assert(recentRejects);
1768+
recentRejects->insert(orphanHash);
1769+
}
1770+
EraseOrphanTx(orphanHash);
1771+
done = true;
1772+
}
1773+
mempool.check(pcoinsTip.get());
1774+
}
1775+
}
1776+
17161777
bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams, CConnman* connman, const std::atomic<bool>& interruptMsgProc, bool enable_bip61)
17171778
{
17181779
LogPrint(BCLog::NET, "received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->GetId());
@@ -2342,8 +2403,6 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
23422403
return true;
23432404
}
23442405

2345-
std::deque<COutPoint> vWorkQueue;
2346-
std::vector<uint256> vEraseQueue;
23472406
CTransactionRef ptx;
23482407
vRecv >> ptx;
23492408
const CTransaction& tx = *ptx;
@@ -2368,7 +2427,12 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
23682427
mempool.check(pcoinsTip.get());
23692428
RelayTransaction(tx, connman);
23702429
for (unsigned int i = 0; i < tx.vout.size(); i++) {
2371-
vWorkQueue.emplace_back(inv.hash, i);
2430+
auto it_by_prev = mapOrphanTransactionsByPrev.find(COutPoint(inv.hash, i));
2431+
if (it_by_prev != mapOrphanTransactionsByPrev.end()) {
2432+
for (const auto& elem : it_by_prev->second) {
2433+
pfrom->orphan_work_set.insert(elem->first);
2434+
}
2435+
}
23722436
}
23732437

23742438
pfrom->nLastTXTime = GetTime();
@@ -2379,65 +2443,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
23792443
mempool.size(), mempool.DynamicMemoryUsage() / 1000);
23802444

23812445
// Recursively process any orphan transactions that depended on this one
2382-
std::set<NodeId> setMisbehaving;
2383-
while (!vWorkQueue.empty()) {
2384-
auto itByPrev = mapOrphanTransactionsByPrev.find(vWorkQueue.front());
2385-
vWorkQueue.pop_front();
2386-
if (itByPrev == mapOrphanTransactionsByPrev.end())
2387-
continue;
2388-
for (auto mi = itByPrev->second.begin();
2389-
mi != itByPrev->second.end();
2390-
++mi)
2391-
{
2392-
const CTransactionRef& porphanTx = (*mi)->second.tx;
2393-
const CTransaction& orphanTx = *porphanTx;
2394-
const uint256& orphanHash = orphanTx.GetHash();
2395-
NodeId fromPeer = (*mi)->second.fromPeer;
2396-
bool fMissingInputs2 = false;
2397-
// Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan
2398-
// resolution (that is, feeding people an invalid transaction based on LegitTxX in order to get
2399-
// anyone relaying LegitTxX banned)
2400-
CValidationState stateDummy;
2401-
2402-
2403-
if (setMisbehaving.count(fromPeer))
2404-
continue;
2405-
if (AcceptToMemoryPool(mempool, stateDummy, porphanTx, &fMissingInputs2, &lRemovedTxn, false /* bypass_limits */, 0 /* nAbsurdFee */)) {
2406-
LogPrint(BCLog::MEMPOOL, " accepted orphan tx %s\n", orphanHash.ToString());
2407-
RelayTransaction(orphanTx, connman);
2408-
for (unsigned int i = 0; i < orphanTx.vout.size(); i++) {
2409-
vWorkQueue.emplace_back(orphanHash, i);
2410-
}
2411-
vEraseQueue.push_back(orphanHash);
2412-
}
2413-
else if (!fMissingInputs2)
2414-
{
2415-
int nDos = 0;
2416-
if (stateDummy.IsInvalid(nDos) && nDos > 0)
2417-
{
2418-
// Punish peer that gave us an invalid orphan tx
2419-
Misbehaving(fromPeer, nDos);
2420-
setMisbehaving.insert(fromPeer);
2421-
LogPrint(BCLog::MEMPOOL, " invalid orphan tx %s\n", orphanHash.ToString());
2422-
}
2423-
// Has inputs but not accepted to mempool
2424-
// Probably non-standard or insufficient fee
2425-
LogPrint(BCLog::MEMPOOL, " removed orphan tx %s\n", orphanHash.ToString());
2426-
vEraseQueue.push_back(orphanHash);
2427-
if (!orphanTx.HasWitness() && !stateDummy.CorruptionPossible()) {
2428-
// Do not use rejection cache for witness transactions or
2429-
// witness-stripped transactions, as they can have been malleated.
2430-
// See https://github.com/bitcoin/bitcoin/issues/8279 for details.
2431-
assert(recentRejects);
2432-
recentRejects->insert(orphanHash);
2433-
}
2434-
}
2435-
mempool.check(pcoinsTip.get());
2436-
}
2437-
}
2438-
2439-
for (const uint256& hash : vEraseQueue)
2440-
EraseOrphanTx(hash);
2446+
ProcessOrphanTx(connman, pfrom->orphan_work_set, lRemovedTxn);
24412447
}
24422448
else if (fMissingInputs)
24432449
{
@@ -3169,11 +3175,21 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
31693175
if (!pfrom->vRecvGetData.empty())
31703176
ProcessGetData(pfrom, chainparams, connman, interruptMsgProc);
31713177

3178+
if (!pfrom->orphan_work_set.empty()) {
3179+
std::list<CTransactionRef> removed_txn;
3180+
LOCK2(cs_main, g_cs_orphans);
3181+
ProcessOrphanTx(connman, pfrom->orphan_work_set, removed_txn);
3182+
for (const CTransactionRef& removedTx : removed_txn) {
3183+
AddToCompactExtraTransactions(removedTx);
3184+
}
3185+
}
3186+
31723187
if (pfrom->fDisconnect)
31733188
return false;
31743189

31753190
// this maintains the order of responses
31763191
if (!pfrom->vRecvGetData.empty()) return true;
3192+
if (!pfrom->orphan_work_set.empty()) return true;
31773193

31783194
// Don't bother if send buffer is too full to respond anyway
31793195
if (pfrom->fPauseSend)

0 commit comments

Comments
 (0)