Changeset View
Changeset View
Standalone View
Standalone View
src/net_processing.cpp
Show First 20 Lines • Show All 2,046 Lines • ▼ Show 20 Lines | if (!ProcessNewBlockHeaders(config, headers, state, &pindexLast, | ||||
++g_outbound_peers_with_protect_from_disconnect; | ++g_outbound_peers_with_protect_from_disconnect; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
void static ProcessOrphanTx(const Config &config, CConnman *connman, | |||||
std::set<TxId> &orphan_work_set) | |||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main, g_cs_orphans) { | |||||
AssertLockHeld(cs_main); | |||||
AssertLockHeld(g_cs_orphans); | |||||
std::unordered_map<NodeId, uint32_t> rejectCountPerNode; | |||||
while (!orphan_work_set.empty()) { | |||||
const TxId orphanTxId = *orphan_work_set.begin(); | |||||
orphan_work_set.erase(orphan_work_set.begin()); | |||||
auto orphan_it = mapOrphanTransactions.find(orphanTxId); | |||||
if (orphan_it == mapOrphanTransactions.end()) { | |||||
continue; | |||||
} | |||||
const CTransactionRef porphanTx = orphan_it->second.tx; | |||||
const CTransaction &orphanTx = *porphanTx; | |||||
NodeId fromPeer = orphan_it->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 it = rejectCountPerNode.find(fromPeer); | |||||
if (it != rejectCountPerNode.end() && | |||||
it->second > MAX_NON_STANDARD_ORPHAN_PER_NODE) { | |||||
continue; | |||||
} | |||||
if (AcceptToMemoryPool(config, g_mempool, stateDummy, porphanTx, | |||||
&fMissingInputs2, false /* bypass_limits */, | |||||
Amount::zero() /* nAbsurdFee */)) { | |||||
LogPrint(BCLog::MEMPOOL, " accepted orphan tx %s\n", | |||||
orphanTxId.ToString()); | |||||
RelayTransaction(orphanTx, connman); | |||||
for (size_t i = 0; i < orphanTx.vout.size(); i++) { | |||||
auto it_by_prev = | |||||
mapOrphanTransactionsByPrev.find(COutPoint(orphanTxId, i)); | |||||
if (it_by_prev != mapOrphanTransactionsByPrev.end()) { | |||||
for (const auto &elem : it_by_prev->second) { | |||||
orphan_work_set.insert(elem->first); | |||||
} | |||||
} | |||||
} | |||||
EraseOrphanTx(orphanTxId); | |||||
} else if (!fMissingInputs2) { | |||||
int nDos = 0; | |||||
if (stateDummy.IsInvalid(nDos)) { | |||||
rejectCountPerNode[fromPeer]++; | |||||
if (nDos > 0) { | |||||
// Punish peer that gave us an invalid orphan tx | |||||
Misbehaving(fromPeer, nDos, "invalid-orphan-tx"); | |||||
LogPrint(BCLog::MEMPOOL, " invalid orphan tx %s\n", | |||||
orphanTxId.ToString()); | |||||
} | |||||
EraseOrphanTx(orphanTxId); | |||||
} | |||||
// Has inputs but not accepted to mempool | |||||
// Probably non-standard or insufficient fee | |||||
LogPrint(BCLog::MEMPOOL, " removed orphan tx %s\n", | |||||
orphanTxId.ToString()); | |||||
if (!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(orphanTxId); | |||||
} | |||||
EraseOrphanTx(orphanTxId); | |||||
} | |||||
g_mempool.check(pcoinsTip.get()); | |||||
} | |||||
} | |||||
static bool ProcessMessage(const Config &config, CNode *pfrom, | static bool ProcessMessage(const Config &config, CNode *pfrom, | ||||
const std::string &strCommand, CDataStream &vRecv, | const std::string &strCommand, CDataStream &vRecv, | ||||
int64_t nTimeReceived, CConnman *connman, | int64_t nTimeReceived, CConnman *connman, | ||||
const std::atomic<bool> &interruptMsgProc, | const std::atomic<bool> &interruptMsgProc, | ||||
bool enable_bip61) { | bool enable_bip61) { | ||||
const CChainParams &chainparams = config.GetChainParams(); | const CChainParams &chainparams = config.GetChainParams(); | ||||
LogPrint(BCLog::NET, "received: %s (%u bytes) peer=%d\n", | LogPrint(BCLog::NET, "received: %s (%u bytes) peer=%d\n", | ||||
SanitizeString(strCommand), vRecv.size(), pfrom->GetId()); | SanitizeString(strCommand), vRecv.size(), pfrom->GetId()); | ||||
▲ Show 20 Lines • Show All 737 Lines • ▼ Show 20 Lines | if (strCommand == NetMsgType::TX) { | ||||
if (!AlreadyHave(inv) && | if (!AlreadyHave(inv) && | ||||
AcceptToMemoryPool(config, g_mempool, state, ptx, &fMissingInputs, | AcceptToMemoryPool(config, g_mempool, state, ptx, &fMissingInputs, | ||||
false /* bypass_limits */, | false /* bypass_limits */, | ||||
Amount::zero() /* nAbsurdFee */)) { | Amount::zero() /* nAbsurdFee */)) { | ||||
g_mempool.check(pcoinsTip.get()); | g_mempool.check(pcoinsTip.get()); | ||||
RelayTransaction(tx, connman); | RelayTransaction(tx, connman); | ||||
for (size_t i = 0; i < tx.vout.size(); i++) { | for (size_t i = 0; i < tx.vout.size(); i++) { | ||||
auto it_by_prev = mapOrphanTransactionsByPrev.find( | auto it_by_prev = | ||||
COutPoint(txid, i)); | mapOrphanTransactionsByPrev.find(COutPoint(txid, i)); | ||||
if (it_by_prev != mapOrphanTransactionsByPrev.end()) { | if (it_by_prev != mapOrphanTransactionsByPrev.end()) { | ||||
for (const auto &elem : it_by_prev->second) { | for (const auto &elem : it_by_prev->second) { | ||||
orphan_work_set.insert(TxId(elem->first)); | orphan_work_set.insert(elem->first); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
pfrom->nLastTXTime = GetTime(); | pfrom->nLastTXTime = GetTime(); | ||||
LogPrint(BCLog::MEMPOOL, | LogPrint(BCLog::MEMPOOL, | ||||
"AcceptToMemoryPool: peer=%d: accepted %s " | "AcceptToMemoryPool: peer=%d: accepted %s " | ||||
"(poolsz %u txn, %u kB)\n", | "(poolsz %u txn, %u kB)\n", | ||||
pfrom->GetId(), tx.GetId().ToString(), g_mempool.size(), | pfrom->GetId(), tx.GetId().ToString(), g_mempool.size(), | ||||
g_mempool.DynamicMemoryUsage() / 1000); | g_mempool.DynamicMemoryUsage() / 1000); | ||||
// Recursively process any orphan transactions that depended on this | // Recursively process any orphan transactions that depended on this | ||||
// one | // one | ||||
std::unordered_map<NodeId, uint32_t> rejectCountPerNode; | ProcessOrphanTx(config, connman, orphan_work_set); | ||||
while (!orphan_work_set.empty()) { | |||||
const TxId orphanTxId = *orphan_work_set.begin(); | |||||
orphan_work_set.erase(orphan_work_set.begin()); | |||||
auto orphan_it = mapOrphanTransactions.find(orphanTxId); | |||||
if (orphan_it == mapOrphanTransactions.end()) { | |||||
continue; | |||||
} | |||||
const CTransactionRef porphanTx = orphan_it->second.tx; | |||||
const CTransaction &orphanTx = *porphanTx; | |||||
NodeId fromPeer = orphan_it->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 it = rejectCountPerNode.find(fromPeer); | |||||
if (it != rejectCountPerNode.end() && | |||||
it->second > MAX_NON_STANDARD_ORPHAN_PER_NODE) { | |||||
continue; | |||||
} | |||||
if (AcceptToMemoryPool(config, g_mempool, stateDummy, porphanTx, | |||||
&fMissingInputs2, | |||||
false /* bypass_limits */, | |||||
Amount::zero() /* nAbsurdFee */)) { | |||||
LogPrint(BCLog::MEMPOOL, " accepted orphan tx %s\n", | |||||
orphanTxId.ToString()); | |||||
RelayTransaction(orphanTx, connman); | |||||
for (size_t i = 0; i < orphanTx.vout.size(); i++) { | |||||
auto it_by_prev = mapOrphanTransactionsByPrev.find( | |||||
COutPoint(orphanTxId, i)); | |||||
if (it_by_prev != mapOrphanTransactionsByPrev.end()) { | |||||
for (const auto &elem : it_by_prev->second) { | |||||
orphan_work_set.insert(TxId(elem->first)); | |||||
} | |||||
} | |||||
} | |||||
EraseOrphanTx(orphanTxId); | |||||
} else if (!fMissingInputs2) { | |||||
int nDos = 0; | |||||
if (stateDummy.IsInvalid(nDos)) { | |||||
rejectCountPerNode[fromPeer]++; | |||||
if (nDos > 0) { | |||||
// Punish peer that gave us an invalid orphan tx | |||||
Misbehaving(fromPeer, nDos, "invalid-orphan-tx"); | |||||
LogPrint(BCLog::MEMPOOL, | |||||
" invalid orphan tx %s\n", | |||||
orphanTxId.ToString()); | |||||
} | |||||
EraseOrphanTx(orphanTxId); | |||||
} | |||||
// Has inputs but not accepted to mempool | |||||
// Probably non-standard or insufficient fee | |||||
LogPrint(BCLog::MEMPOOL, " removed orphan tx %s\n", | |||||
orphanTxId.ToString()); | |||||
if (!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(orphanTxId); | |||||
} | |||||
EraseOrphanTx(orphanTxId); | |||||
} | |||||
g_mempool.check(pcoinsTip.get()); | |||||
} | |||||
} else if (fMissingInputs) { | } else if (fMissingInputs) { | ||||
// It may be the case that the orphans parents have all been | // It may be the case that the orphans parents have all been | ||||
// rejected. | // rejected. | ||||
bool fRejectedParents = false; | bool fRejectedParents = false; | ||||
for (const CTxIn &txin : tx.vin) { | for (const CTxIn &txin : tx.vin) { | ||||
if (recentRejects->contains(txin.prevout.GetTxId())) { | if (recentRejects->contains(txin.prevout.GetTxId())) { | ||||
fRejectedParents = true; | fRejectedParents = true; | ||||
break; | break; | ||||
▲ Show 20 Lines • Show All 1,981 Lines • Show Last 20 Lines |