Simplify orphan processing in preparation for interruptibility

This commit is contained in:
Pieter Wuille 2019-03-19 18:06:35 -07:00
parent 68520597cc
commit 9453018fdc

View file

@ -2342,8 +2342,8 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
return true; return true;
} }
std::deque<COutPoint> vWorkQueue; std::set<uint256> orphan_work_set;
std::vector<uint256> vEraseQueue;
CTransactionRef ptx; CTransactionRef ptx;
vRecv >> ptx; vRecv >> ptx;
const CTransaction& tx = *ptx; const CTransaction& tx = *ptx;
@ -2368,7 +2368,12 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
mempool.check(pcoinsTip.get()); mempool.check(pcoinsTip.get());
RelayTransaction(tx, connman); RelayTransaction(tx, connman);
for (unsigned int i = 0; i < tx.vout.size(); i++) { for (unsigned int i = 0; i < tx.vout.size(); i++) {
vWorkQueue.emplace_back(inv.hash, i); auto it_by_prev = mapOrphanTransactionsByPrev.find(COutPoint(inv.hash, i));
if (it_by_prev != mapOrphanTransactionsByPrev.end()) {
for (const auto& elem : it_by_prev->second) {
orphan_work_set.insert(elem->first);
}
}
} }
pfrom->nLastTXTime = GetTime(); pfrom->nLastTXTime = GetTime();
@ -2380,64 +2385,57 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// Recursively process any orphan transactions that depended on this one // Recursively process any orphan transactions that depended on this one
std::set<NodeId> setMisbehaving; std::set<NodeId> setMisbehaving;
while (!vWorkQueue.empty()) { while (!orphan_work_set.empty()) {
auto itByPrev = mapOrphanTransactionsByPrev.find(vWorkQueue.front()); const uint256 orphanHash = *orphan_work_set.begin();
vWorkQueue.pop_front(); orphan_work_set.erase(orphan_work_set.begin());
if (itByPrev == mapOrphanTransactionsByPrev.end())
continue;
for (auto mi = itByPrev->second.begin();
mi != itByPrev->second.end();
++mi)
{
const CTransactionRef& porphanTx = (*mi)->second.tx;
const CTransaction& orphanTx = *porphanTx;
const uint256& orphanHash = orphanTx.GetHash();
NodeId fromPeer = (*mi)->second.fromPeer;
bool fMissingInputs2 = false;
// Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan
// resolution (that is, feeding people an invalid transaction based on LegitTxX in order to get
// anyone relaying LegitTxX banned)
CValidationState stateDummy;
auto orphan_it = mapOrphanTransactions.find(orphanHash);
if (orphan_it == mapOrphanTransactions.end()) continue;
if (setMisbehaving.count(fromPeer)) const CTransactionRef porphanTx = orphan_it->second.tx;
continue; const CTransaction& orphanTx = *porphanTx;
if (AcceptToMemoryPool(mempool, stateDummy, porphanTx, &fMissingInputs2, &lRemovedTxn, false /* bypass_limits */, 0 /* nAbsurdFee */)) { NodeId fromPeer = orphan_it->second.fromPeer;
LogPrint(BCLog::MEMPOOL, " accepted orphan tx %s\n", orphanHash.ToString()); bool fMissingInputs2 = false;
RelayTransaction(orphanTx, connman); // Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan
for (unsigned int i = 0; i < orphanTx.vout.size(); i++) { // resolution (that is, feeding people an invalid transaction based on LegitTxX in order to get
vWorkQueue.emplace_back(orphanHash, i); // anyone relaying LegitTxX banned)
} CValidationState stateDummy;
vEraseQueue.push_back(orphanHash);
} if (setMisbehaving.count(fromPeer)) continue;
else if (!fMissingInputs2) if (AcceptToMemoryPool(mempool, stateDummy, porphanTx, &fMissingInputs2, &lRemovedTxn, false /* bypass_limits */, 0 /* nAbsurdFee */)) {
{ LogPrint(BCLog::MEMPOOL, " accepted orphan tx %s\n", orphanHash.ToString());
int nDos = 0; RelayTransaction(orphanTx, connman);
if (stateDummy.IsInvalid(nDos) && nDos > 0) for (unsigned int i = 0; i < orphanTx.vout.size(); i++) {
{ auto it_by_prev = mapOrphanTransactionsByPrev.find(COutPoint(orphanHash, i));
// Punish peer that gave us an invalid orphan tx if (it_by_prev != mapOrphanTransactionsByPrev.end()) {
Misbehaving(fromPeer, nDos); for (const auto& elem : it_by_prev->second) {
setMisbehaving.insert(fromPeer); orphan_work_set.insert(elem->first);
LogPrint(BCLog::MEMPOOL, " invalid orphan tx %s\n", orphanHash.ToString()); }
}
// Has inputs but not accepted to mempool
// Probably non-standard or insufficient fee
LogPrint(BCLog::MEMPOOL, " removed orphan tx %s\n", orphanHash.ToString());
vEraseQueue.push_back(orphanHash);
if (!orphanTx.HasWitness() && !stateDummy.CorruptionPossible()) {
// Do not use rejection cache for witness transactions or
// witness-stripped transactions, as they can have been malleated.
// See https://github.com/bitcoin/bitcoin/issues/8279 for details.
assert(recentRejects);
recentRejects->insert(orphanHash);
} }
} }
mempool.check(pcoinsTip.get()); EraseOrphanTx(orphanHash);
} else if (!fMissingInputs2) {
int nDos = 0;
if (stateDummy.IsInvalid(nDos) && nDos > 0) {
// Punish peer that gave us an invalid orphan tx
Misbehaving(fromPeer, nDos);
setMisbehaving.insert(fromPeer);
LogPrint(BCLog::MEMPOOL, " invalid orphan tx %s\n", orphanHash.ToString());
}
// Has inputs but not accepted to mempool
// Probably non-standard or insufficient fee
LogPrint(BCLog::MEMPOOL, " removed orphan tx %s\n", orphanHash.ToString());
if (!orphanTx.HasWitness() && !stateDummy.CorruptionPossible()) {
// Do not use rejection cache for witness transactions or
// witness-stripped transactions, as they can have been malleated.
// See https://github.com/bitcoin/bitcoin/issues/8279 for details.
assert(recentRejects);
recentRejects->insert(orphanHash);
}
EraseOrphanTx(orphanHash);
} }
mempool.check(pcoinsTip.get());
} }
for (const uint256& hash : vEraseQueue)
EraseOrphanTx(hash);
} }
else if (fMissingInputs) else if (fMissingInputs)
{ {