Changeset View
Changeset View
Standalone View
Standalone View
src/net_processing.cpp
Show First 20 Lines • Show All 720 Lines • ▼ Show 20 Lines | void AddToCompactExtraTransactions(const CTransactionRef &tx) { | ||||
vExtraTxnForCompact[vExtraTxnForCompactIt] = | vExtraTxnForCompact[vExtraTxnForCompactIt] = | ||||
std::make_pair(tx->GetHash(), tx); | std::make_pair(tx->GetHash(), tx); | ||||
vExtraTxnForCompactIt = (vExtraTxnForCompactIt + 1) % max_extra_txn; | vExtraTxnForCompactIt = (vExtraTxnForCompactIt + 1) % max_extra_txn; | ||||
} | } | ||||
bool AddOrphanTx(const CTransactionRef &tx, NodeId peer) | bool AddOrphanTx(const CTransactionRef &tx, NodeId peer) | ||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | ||||
const uint256 &txid = tx->GetId(); | const uint256 &txhash = tx->GetHash(); | ||||
if (mapOrphanTransactions.count(txid)) { | if (mapOrphanTransactions.count(txhash)) { | ||||
return false; | return false; | ||||
} | } | ||||
// Ignore big transactions, to avoid a send-big-orphans memory exhaustion | // Ignore big transactions, to avoid a send-big-orphans memory exhaustion | ||||
// attack. If a peer has a legitimate large transaction with a missing | // attack. If a peer has a legitimate large transaction with a missing | ||||
// parent then we assume it will rebroadcast it later, after the parent | // parent then we assume it will rebroadcast it later, after the parent | ||||
// transaction(s) have been mined or received. | // transaction(s) have been mined or received. | ||||
// 100 orphans, each of which is at most 99,999 bytes big is at most 10 | // 100 orphans, each of which is at most 99,999 bytes big is at most 10 | ||||
// megabytes of orphans and somewhat more byprev index (in the worst case): | // megabytes of orphans and somewhat more byprev index (in the worst case): | ||||
unsigned int sz = GetTransactionSize(*tx); | unsigned int sz = GetTransactionSize(*tx); | ||||
if (sz >= MAX_STANDARD_TX_SIZE) { | if (sz >= MAX_STANDARD_TX_SIZE) { | ||||
LogPrint("mempool", "ignoring large orphan tx (size: %u, hash: %s)\n", | LogPrint("mempool", "ignoring large orphan tx (size: %u, hash: %s)\n", | ||||
sz, txid.ToString()); | sz, txhash.ToString()); | ||||
return false; | return false; | ||||
} | } | ||||
auto ret = mapOrphanTransactions.emplace( | auto ret = mapOrphanTransactions.emplace( | ||||
txid, COrphanTx{tx, peer, GetTime() + ORPHAN_TX_EXPIRE_TIME}); | txhash, COrphanTx{tx, peer, GetTime() + ORPHAN_TX_EXPIRE_TIME}); | ||||
assert(ret.second); | assert(ret.second); | ||||
for (const CTxIn &txin : tx->vin) { | for (const CTxIn &txin : tx->vin) { | ||||
mapOrphanTransactionsByPrev[txin.prevout].insert(ret.first); | mapOrphanTransactionsByPrev[txin.prevout].insert(ret.first); | ||||
} | } | ||||
AddToCompactExtraTransactions(tx); | AddToCompactExtraTransactions(tx); | ||||
LogPrint("mempool", "stored orphan tx %s (mapsz %u outsz %u)\n", | LogPrint("mempool", "stored orphan tx %s (mapsz %u outsz %u)\n", | ||||
txid.ToString(), mapOrphanTransactions.size(), | txhash.ToString(), mapOrphanTransactions.size(), | ||||
mapOrphanTransactionsByPrev.size()); | mapOrphanTransactionsByPrev.size()); | ||||
return true; | return true; | ||||
} | } | ||||
static int EraseOrphanTx(uint256 hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | static int EraseOrphanTx(uint256 hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | ||||
std::map<uint256, COrphanTx>::iterator it = | std::map<uint256, COrphanTx>::iterator it = | ||||
mapOrphanTransactions.find(hash); | mapOrphanTransactions.find(hash); | ||||
if (it == mapOrphanTransactions.end()) { | if (it == mapOrphanTransactions.end()) { | ||||
Show All 15 Lines | |||||
void EraseOrphansFor(NodeId peer) { | void EraseOrphansFor(NodeId peer) { | ||||
int nErased = 0; | int nErased = 0; | ||||
std::map<uint256, COrphanTx>::iterator iter = mapOrphanTransactions.begin(); | std::map<uint256, COrphanTx>::iterator iter = mapOrphanTransactions.begin(); | ||||
while (iter != mapOrphanTransactions.end()) { | while (iter != mapOrphanTransactions.end()) { | ||||
// Increment to avoid iterator becoming invalid. | // Increment to avoid iterator becoming invalid. | ||||
std::map<uint256, COrphanTx>::iterator maybeErase = iter++; | std::map<uint256, COrphanTx>::iterator maybeErase = iter++; | ||||
if (maybeErase->second.fromPeer == peer) { | if (maybeErase->second.fromPeer == peer) { | ||||
nErased += EraseOrphanTx(maybeErase->second.tx->GetId()); | nErased += EraseOrphanTx(maybeErase->second.tx->GetHash()); | ||||
} | } | ||||
} | } | ||||
if (nErased > 0) { | if (nErased > 0) { | ||||
LogPrint("mempool", "Erased %d orphan tx from peer=%d\n", nErased, | LogPrint("mempool", "Erased %d orphan tx from peer=%d\n", nErased, | ||||
peer); | peer); | ||||
} | } | ||||
} | } | ||||
unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) | unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) | ||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | ||||
unsigned int nEvicted = 0; | unsigned int nEvicted = 0; | ||||
static int64_t nNextSweep; | static int64_t nNextSweep; | ||||
int64_t nNow = GetTime(); | int64_t nNow = GetTime(); | ||||
if (nNextSweep <= nNow) { | if (nNextSweep <= nNow) { | ||||
// Sweep out expired orphan pool entries: | // Sweep out expired orphan pool entries: | ||||
int nErased = 0; | int nErased = 0; | ||||
int64_t nMinExpTime = | int64_t nMinExpTime = | ||||
nNow + ORPHAN_TX_EXPIRE_TIME - ORPHAN_TX_EXPIRE_INTERVAL; | nNow + ORPHAN_TX_EXPIRE_TIME - ORPHAN_TX_EXPIRE_INTERVAL; | ||||
std::map<uint256, COrphanTx>::iterator iter = | std::map<uint256, COrphanTx>::iterator iter = | ||||
mapOrphanTransactions.begin(); | mapOrphanTransactions.begin(); | ||||
while (iter != mapOrphanTransactions.end()) { | while (iter != mapOrphanTransactions.end()) { | ||||
std::map<uint256, COrphanTx>::iterator maybeErase = iter++; | std::map<uint256, COrphanTx>::iterator maybeErase = iter++; | ||||
if (maybeErase->second.nTimeExpire <= nNow) { | if (maybeErase->second.nTimeExpire <= nNow) { | ||||
nErased += EraseOrphanTx(maybeErase->second.tx->GetId()); | nErased += EraseOrphanTx(maybeErase->second.tx->GetHash()); | ||||
} else { | } else { | ||||
nMinExpTime = | nMinExpTime = | ||||
std::min(maybeErase->second.nTimeExpire, nMinExpTime); | std::min(maybeErase->second.nTimeExpire, nMinExpTime); | ||||
} | } | ||||
} | } | ||||
// Sweep again 5 minutes after the next entry that expires in order to | // Sweep again 5 minutes after the next entry that expires in order to | ||||
// batch the linear scan. | // batch the linear scan. | ||||
nNextSweep = nMinExpTime + ORPHAN_TX_EXPIRE_INTERVAL; | nNextSweep = nMinExpTime + ORPHAN_TX_EXPIRE_INTERVAL; | ||||
▲ Show 20 Lines • Show All 73 Lines • ▼ Show 20 Lines | void PeerLogicValidation::SyncTransaction(const CTransaction &tx, | ||||
for (size_t j = 0; j < tx.vin.size(); j++) { | for (size_t j = 0; j < tx.vin.size(); j++) { | ||||
auto itByPrev = mapOrphanTransactionsByPrev.find(tx.vin[j].prevout); | auto itByPrev = mapOrphanTransactionsByPrev.find(tx.vin[j].prevout); | ||||
if (itByPrev == mapOrphanTransactionsByPrev.end()) { | if (itByPrev == mapOrphanTransactionsByPrev.end()) { | ||||
continue; | continue; | ||||
} | } | ||||
for (auto mi = itByPrev->second.begin(); mi != itByPrev->second.end(); | for (auto mi = itByPrev->second.begin(); mi != itByPrev->second.end(); | ||||
++mi) { | ++mi) { | ||||
const CTransaction &orphanTx = *(*mi)->second.tx; | const CTransaction &orphanTx = *(*mi)->second.tx; | ||||
const uint256 &orphanId = orphanTx.GetId(); | const uint256 &orphanId = orphanTx.GetHash(); | ||||
vOrphanErase.push_back(orphanId); | vOrphanErase.push_back(orphanId); | ||||
} | } | ||||
} | } | ||||
// Erase orphan transactions include or precluded by this block | // Erase orphan transactions include or precluded by this block | ||||
if (vOrphanErase.size()) { | if (vOrphanErase.size()) { | ||||
int nErased = 0; | int nErased = 0; | ||||
for (uint256 &orphanId : vOrphanErase) { | for (uint256 &orphanId : vOrphanErase) { | ||||
nErased += EraseOrphanTx(orphanId); | nErased += EraseOrphanTx(orphanId); | ||||
} | } | ||||
LogPrint("mempool", | LogPrint("mempool", | ||||
"Erased %d orphan tx included or conflicted by block\n", | "Erased %d orphan tx included or conflicted by block\n", | ||||
nErased); | nErased); | ||||
} | } | ||||
} | } | ||||
static CCriticalSection cs_most_recent_block; | static CCriticalSection cs_most_recent_block; | ||||
static std::shared_ptr<const CBlock> most_recent_block; | static std::shared_ptr<const CBlock> most_recent_block; | ||||
static std::shared_ptr<const CBlockHeaderAndShortTxIDs> | static std::shared_ptr<const CBlockHeaderAndShortTxHashes> | ||||
most_recent_compact_block; | most_recent_compact_block; | ||||
static uint256 most_recent_block_hash; | static uint256 most_recent_block_hash; | ||||
void PeerLogicValidation::NewPoWValidBlock( | void PeerLogicValidation::NewPoWValidBlock( | ||||
const CBlockIndex *pindex, const std::shared_ptr<const CBlock> &pblock) { | const CBlockIndex *pindex, const std::shared_ptr<const CBlock> &pblock) { | ||||
std::shared_ptr<const CBlockHeaderAndShortTxIDs> pcmpctblock = | std::shared_ptr<const CBlockHeaderAndShortTxHashes> pcmpctblock = | ||||
std::make_shared<const CBlockHeaderAndShortTxIDs>(*pblock); | std::make_shared<const CBlockHeaderAndShortTxHashes>(*pblock); | ||||
const CNetMsgMaker msgMaker(PROTOCOL_VERSION); | const CNetMsgMaker msgMaker(PROTOCOL_VERSION); | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
static int nHighestFastAnnounce = 0; | static int nHighestFastAnnounce = 0; | ||||
if (pindex->nHeight <= nHighestFastAnnounce) { | if (pindex->nHeight <= nHighestFastAnnounce) { | ||||
return; | return; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 112 Lines • ▼ Show 20 Lines | |||||
////////////////////////////////////////////////////////////////////////////// | ////////////////////////////////////////////////////////////////////////////// | ||||
// | // | ||||
// Messages | // Messages | ||||
// | // | ||||
static bool AlreadyHave(const CInv &inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | static bool AlreadyHave(const CInv &inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | ||||
switch (inv.type) { | switch (inv.type) { | ||||
case MSG_TX: { | case MSG_TX: { | ||||
const txhash_t txhash(inv.hash); | |||||
assert(recentRejects); | assert(recentRejects); | ||||
if (chainActive.Tip()->GetBlockHash() != | if (chainActive.Tip()->GetBlockHash() != | ||||
hashRecentRejectsChainTip) { | hashRecentRejectsChainTip) { | ||||
// If the chain tip has changed previously rejected transactions | // If the chain tip has changed previously rejected transactions | ||||
// might be now valid, e.g. due to a nLockTime'd tx becoming | // might be now valid, e.g. due to a nLockTime'd tx becoming | ||||
// valid, or a double-spend. Reset the rejects filter and give | // valid, or a double-spend. Reset the rejects filter and give | ||||
// those txs a second chance. | // those txs a second chance. | ||||
hashRecentRejectsChainTip = chainActive.Tip()->GetBlockHash(); | hashRecentRejectsChainTip = chainActive.Tip()->GetBlockHash(); | ||||
recentRejects->reset(); | recentRejects->reset(); | ||||
} | } | ||||
return recentRejects->contains(inv.hash) || | return recentRejects->contains(inv.hash) || | ||||
mempool.exists(inv.hash) || | mempool.exists(inv.hash) || | ||||
mapOrphanTransactions.count(inv.hash); | mapOrphanTransactions.count(inv.hash); | ||||
} | } | ||||
case MSG_BLOCK: | case MSG_BLOCK: | ||||
return mapBlockIndex.count(inv.hash); | return mapBlockIndex.count(inv.hash); | ||||
} | } | ||||
// Don't know what it is, just say we already got one | // Don't know what it is, just say we already got one | ||||
return true; | return true; | ||||
} | } | ||||
static void RelayTransaction(const CTransaction &tx, CConnman &connman) { | static void RelayTransaction(const CTransaction &tx, CConnman &connman) { | ||||
CInv inv(MSG_TX, tx.GetId()); | CInv inv(MSG_TX, tx.GetHash()); | ||||
connman.ForEachNode([&inv](CNode *pnode) { pnode->PushInventory(inv); }); | connman.ForEachNode([&inv](CNode *pnode) { pnode->PushInventory(inv); }); | ||||
} | } | ||||
static void RelayAddress(const CAddress &addr, bool fReachable, | static void RelayAddress(const CAddress &addr, bool fReachable, | ||||
CConnman &connman) { | CConnman &connman) { | ||||
// Limited relaying of addresses outside our network(s) | // Limited relaying of addresses outside our network(s) | ||||
unsigned int nRelayNodes = fReachable ? 2 : 1; | unsigned int nRelayNodes = fReachable ? 2 : 1; | ||||
▲ Show 20 Lines • Show All 180 Lines • ▼ Show 20 Lines | while (it != pfrom->vRecvGetData.end()) { | ||||
// guaranteed they won't have a useful mempool to match | // guaranteed they won't have a useful mempool to match | ||||
// against a compact block, and we don't feel like | // against a compact block, and we don't feel like | ||||
// constructing the object for them, so instead we | // constructing the object for them, so instead we | ||||
// respond with the full, non-compact block. | // respond with the full, non-compact block. | ||||
int nSendFlags = 0; | int nSendFlags = 0; | ||||
if (CanDirectFetch(consensusParams) && | if (CanDirectFetch(consensusParams) && | ||||
mi->second->nHeight >= | mi->second->nHeight >= | ||||
chainActive.Height() - MAX_CMPCTBLOCK_DEPTH) { | chainActive.Height() - MAX_CMPCTBLOCK_DEPTH) { | ||||
CBlockHeaderAndShortTxIDs cmpctblock(block); | CBlockHeaderAndShortTxHashes cmpctblock(block); | ||||
connman.PushMessage( | connman.PushMessage( | ||||
pfrom, msgMaker.Make(nSendFlags, | pfrom, msgMaker.Make(nSendFlags, | ||||
NetMsgType::CMPCTBLOCK, | NetMsgType::CMPCTBLOCK, | ||||
cmpctblock)); | cmpctblock)); | ||||
} else { | } else { | ||||
connman.PushMessage( | connman.PushMessage( | ||||
pfrom, msgMaker.Make(nSendFlags, | pfrom, msgMaker.Make(nSendFlags, | ||||
NetMsgType::BLOCK, block)); | NetMsgType::BLOCK, block)); | ||||
Show All 10 Lines | while (it != pfrom->vRecvGetData.end()) { | ||||
vInv.push_back( | vInv.push_back( | ||||
CInv(MSG_BLOCK, chainActive.Tip()->GetBlockHash())); | CInv(MSG_BLOCK, chainActive.Tip()->GetBlockHash())); | ||||
connman.PushMessage( | connman.PushMessage( | ||||
pfrom, msgMaker.Make(NetMsgType::INV, vInv)); | pfrom, msgMaker.Make(NetMsgType::INV, vInv)); | ||||
pfrom->hashContinue.SetNull(); | pfrom->hashContinue.SetNull(); | ||||
} | } | ||||
} | } | ||||
} else if (inv.type == MSG_TX) { | } else if (inv.type == MSG_TX) { | ||||
const txhash_t txhash(inv.hash); | |||||
// Send stream from relay memory | // Send stream from relay memory | ||||
bool push = false; | bool push = false; | ||||
auto mi = mapRelay.find(inv.hash); | auto mi = mapRelay.find(inv.hash); | ||||
int nSendFlags = 0; | int nSendFlags = 0; | ||||
if (mi != mapRelay.end()) { | if (mi != mapRelay.end()) { | ||||
connman.PushMessage( | connman.PushMessage( | ||||
pfrom, | pfrom, | ||||
msgMaker.Make(nSendFlags, NetMsgType::TX, *mi->second)); | msgMaker.Make(nSendFlags, NetMsgType::TX, *mi->second)); | ||||
push = true; | push = true; | ||||
} else if (pfrom->timeLastMempoolReq) { | } else if (pfrom->timeLastMempoolReq) { | ||||
auto txinfo = mempool.info(inv.hash); | auto txinfo = mempool.info(txhash); | ||||
// To protect privacy, do not answer getdata using the | // To protect privacy, do not answer getdata using the | ||||
// mempool when that TX couldn't have been INVed in reply to | // mempool when that TX couldn't have been INVed in reply to | ||||
// a MEMPOOL request. | // a MEMPOOL request. | ||||
if (txinfo.tx && | if (txinfo.tx && | ||||
txinfo.nTime <= pfrom->timeLastMempoolReq) { | txinfo.nTime <= pfrom->timeLastMempoolReq) { | ||||
connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, | connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, | ||||
NetMsgType::TX, | NetMsgType::TX, | ||||
*txinfo.tx)); | *txinfo.tx)); | ||||
▲ Show 20 Lines • Show All 733 Lines • ▼ Show 20 Lines | else if (strCommand == NetMsgType::TX) { | ||||
} | } | ||||
std::deque<COutPoint> vWorkQueue; | std::deque<COutPoint> vWorkQueue; | ||||
std::vector<uint256> vEraseQueue; | std::vector<uint256> vEraseQueue; | ||||
CTransactionRef ptx; | CTransactionRef ptx; | ||||
vRecv >> ptx; | vRecv >> ptx; | ||||
const CTransaction &tx = *ptx; | const CTransaction &tx = *ptx; | ||||
CInv inv(MSG_TX, tx.GetId()); | CInv inv(MSG_TX, tx.GetHash()); | ||||
pfrom->AddInventoryKnown(inv); | pfrom->AddInventoryKnown(inv); | ||||
const unspentid_t &unspentid = tx.Getunspentid(); | |||||
LOCK(cs_main); | LOCK(cs_main); | ||||
bool fMissingInputs = false; | bool fMissingInputs = false; | ||||
CValidationState state; | CValidationState state; | ||||
pfrom->setAskFor.erase(inv.hash); | pfrom->setAskFor.erase(inv.hash); | ||||
mapAlreadyAskedFor.erase(inv.hash); | mapAlreadyAskedFor.erase(inv.hash); | ||||
std::list<CTransactionRef> lRemovedTxn; | std::list<CTransactionRef> lRemovedTxn; | ||||
if (!AlreadyHave(inv) && | if (!AlreadyHave(inv) && | ||||
AcceptToMemoryPool(config, mempool, state, ptx, true, | AcceptToMemoryPool(config, mempool, state, ptx, true, | ||||
&fMissingInputs, &lRemovedTxn)) { | &fMissingInputs, &lRemovedTxn)) { | ||||
mempool.check(pcoinsTip); | mempool.check(pcoinsTip); | ||||
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++) { | ||||
vWorkQueue.emplace_back(inv.hash, i); | vWorkQueue.emplace_back(unspentid, i); | ||||
} | } | ||||
pfrom->nLastTXTime = GetTime(); | pfrom->nLastTXTime = GetTime(); | ||||
LogPrint("mempool", "AcceptToMemoryPool: peer=%d: accepted %s " | LogPrint("mempool", "AcceptToMemoryPool: peer=%d: accepted %s " | ||||
"(poolsz %u txn, %u kB)\n", | "(poolsz %u txn, %u kB)\n", | ||||
pfrom->id, tx.GetId().ToString(), mempool.size(), | pfrom->id, tx.GetHash().ToString(), mempool.size(), | ||||
mempool.DynamicMemoryUsage() / 1000); | mempool.DynamicMemoryUsage() / 1000); | ||||
// Recursively process any orphan transactions that depended on this | // Recursively process any orphan transactions that depended on this | ||||
// one | // one | ||||
std::set<NodeId> setMisbehaving; | std::set<NodeId> setMisbehaving; | ||||
while (!vWorkQueue.empty()) { | while (!vWorkQueue.empty()) { | ||||
auto itByPrev = | auto itByPrev = | ||||
mapOrphanTransactionsByPrev.find(vWorkQueue.front()); | mapOrphanTransactionsByPrev.find(vWorkQueue.front()); | ||||
vWorkQueue.pop_front(); | vWorkQueue.pop_front(); | ||||
if (itByPrev == mapOrphanTransactionsByPrev.end()) { | if (itByPrev == mapOrphanTransactionsByPrev.end()) { | ||||
continue; | continue; | ||||
} | } | ||||
for (auto mi = itByPrev->second.begin(); | for (auto mi = itByPrev->second.begin(); | ||||
mi != itByPrev->second.end(); ++mi) { | mi != itByPrev->second.end(); ++mi) { | ||||
const CTransactionRef &porphanTx = (*mi)->second.tx; | const CTransactionRef &porphanTx = (*mi)->second.tx; | ||||
const CTransaction &orphanTx = *porphanTx; | const CTransaction &orphanTx = *porphanTx; | ||||
const uint256 &orphanId = orphanTx.GetId(); | const uint256 &orphanId = orphanTx.GetHash(); | ||||
const unspentid_t &orphanunspentid = | |||||
orphanTx.Getunspentid(); | |||||
NodeId fromPeer = (*mi)->second.fromPeer; | NodeId fromPeer = (*mi)->second.fromPeer; | ||||
bool fMissingInputs2 = false; | bool fMissingInputs2 = false; | ||||
// Use a dummy CValidationState so someone can't setup nodes | // Use a dummy CValidationState so someone can't setup nodes | ||||
// to counter-DoS based on orphan resolution (that is, | // to counter-DoS based on orphan resolution (that is, | ||||
// feeding people an invalid transaction based on LegitTxX | // feeding people an invalid transaction based on LegitTxX | ||||
// in order to get anyone relaying LegitTxX banned) | // in order to get anyone relaying LegitTxX banned) | ||||
CValidationState stateDummy; | CValidationState stateDummy; | ||||
if (setMisbehaving.count(fromPeer)) { | if (setMisbehaving.count(fromPeer)) { | ||||
continue; | continue; | ||||
} | } | ||||
if (AcceptToMemoryPool(config, mempool, stateDummy, | if (AcceptToMemoryPool(config, mempool, stateDummy, | ||||
porphanTx, true, &fMissingInputs2, | porphanTx, true, &fMissingInputs2, | ||||
&lRemovedTxn)) { | &lRemovedTxn)) { | ||||
LogPrint("mempool", " accepted orphan tx %s\n", | LogPrint("mempool", " accepted orphan tx %s\n", | ||||
orphanId.ToString()); | orphanunspentid.ToString()); | ||||
RelayTransaction(orphanTx, connman); | RelayTransaction(orphanTx, connman); | ||||
for (size_t i = 0; i < orphanTx.vout.size(); i++) { | for (size_t i = 0; i < orphanTx.vout.size(); i++) { | ||||
vWorkQueue.emplace_back(orphanId, i); | vWorkQueue.emplace_back(orphanunspentid, i); | ||||
} | } | ||||
vEraseQueue.push_back(orphanId); | vEraseQueue.push_back(orphanId); | ||||
} else if (!fMissingInputs2) { | } else if (!fMissingInputs2) { | ||||
int nDos = 0; | int nDos = 0; | ||||
if (stateDummy.IsInvalid(nDos) && nDos > 0) { | if (stateDummy.IsInvalid(nDos) && nDos > 0) { | ||||
// Punish peer that gave us an invalid orphan tx | // Punish peer that gave us an invalid orphan tx | ||||
Misbehaving(fromPeer, nDos, "invalid-orphan-tx"); | Misbehaving(fromPeer, nDos, "invalid-orphan-tx"); | ||||
setMisbehaving.insert(fromPeer); | setMisbehaving.insert(fromPeer); | ||||
Show All 22 Lines | else if (strCommand == NetMsgType::TX) { | ||||
for (uint256 hash : vEraseQueue) { | for (uint256 hash : vEraseQueue) { | ||||
EraseOrphanTx(hash); | EraseOrphanTx(hash); | ||||
} | } | ||||
} 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.hash)) { | if (recentRejects->contains(txin.prevout.unspentid)) { | ||||
fRejectedParents = true; | fRejectedParents = true; | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
if (!fRejectedParents) { | if (!fRejectedParents) { | ||||
uint32_t nFetchFlags = GetFetchFlags( | uint32_t nFetchFlags = GetFetchFlags( | ||||
pfrom, chainActive.Tip(), chainparams.GetConsensus()); | pfrom, chainActive.Tip(), chainparams.GetConsensus()); | ||||
for (const CTxIn &txin : tx.vin) { | for (const CTxIn &txin : tx.vin) { | ||||
CInv _inv(MSG_TX | nFetchFlags, txin.prevout.hash); | CInv _inv(MSG_TX | nFetchFlags, txin.prevout.unspentid); | ||||
pfrom->AddInventoryKnown(_inv); | pfrom->AddInventoryKnown(_inv); | ||||
if (!AlreadyHave(_inv)) { | if (!AlreadyHave(_inv)) { | ||||
pfrom->AskFor(_inv); | pfrom->AskFor(_inv); | ||||
} | } | ||||
} | } | ||||
AddOrphanTx(ptx, pfrom->GetId()); | AddOrphanTx(ptx, pfrom->GetId()); | ||||
// DoS prevention: do not allow mapOrphanTransactions to grow | // DoS prevention: do not allow mapOrphanTransactions to grow | ||||
// unbounded | // unbounded | ||||
unsigned int nMaxOrphanTx = (unsigned int)std::max( | unsigned int nMaxOrphanTx = (unsigned int)std::max( | ||||
int64_t(0), | int64_t(0), | ||||
GetArg("-maxorphantx", DEFAULT_MAX_ORPHAN_TRANSACTIONS)); | GetArg("-maxorphantx", DEFAULT_MAX_ORPHAN_TRANSACTIONS)); | ||||
unsigned int nEvicted = LimitOrphanTxSize(nMaxOrphanTx); | unsigned int nEvicted = LimitOrphanTxSize(nMaxOrphanTx); | ||||
if (nEvicted > 0) { | if (nEvicted > 0) { | ||||
LogPrint("mempool", "mapOrphan overflow, removed %u tx\n", | LogPrint("mempool", "mapOrphan overflow, removed %u tx\n", | ||||
nEvicted); | nEvicted); | ||||
} | } | ||||
} else { | } else { | ||||
LogPrint("mempool", | LogPrint("mempool", | ||||
"not keeping orphan with rejected parents %s\n", | "not keeping orphan with rejected parents %s\n", | ||||
tx.GetId().ToString()); | tx.GetHash().ToString()); | ||||
// We will continue to reject this tx since it has rejected | // We will continue to reject this tx since it has rejected | ||||
// parents so avoid re-requesting it from other peers. | // parents so avoid re-requesting it from other peers. | ||||
recentRejects->insert(tx.GetId()); | recentRejects->insert(tx.GetHash()); | ||||
} | } | ||||
} else { | } else { | ||||
if (!state.CorruptionPossible()) { | if (!state.CorruptionPossible()) { | ||||
// Do not use rejection cache for witness transactions or | // Do not use rejection cache for witness transactions or | ||||
// witness-stripped transactions, as they can have been | // witness-stripped transactions, as they can have been | ||||
// malleated. See https://github.com/bitcoin/bitcoin/issues/8279 | // malleated. See https://github.com/bitcoin/bitcoin/issues/8279 | ||||
// for details. | // for details. | ||||
assert(recentRejects); | assert(recentRejects); | ||||
recentRejects->insert(tx.GetId()); | recentRejects->insert(inv.hash); | ||||
if (RecursiveDynamicUsage(*ptx) < 100000) { | if (RecursiveDynamicUsage(*ptx) < 100000) { | ||||
AddToCompactExtraTransactions(ptx); | AddToCompactExtraTransactions(ptx); | ||||
} | } | ||||
} | } | ||||
if (pfrom->fWhitelisted && | if (pfrom->fWhitelisted && | ||||
GetBoolArg("-whitelistforcerelay", | GetBoolArg("-whitelistforcerelay", | ||||
DEFAULT_WHITELISTFORCERELAY)) { | DEFAULT_WHITELISTFORCERELAY)) { | ||||
// Always relay transactions received from whitelisted peers, | // Always relay transactions received from whitelisted peers, | ||||
// even if they were already in the mempool or rejected from it | // even if they were already in the mempool or rejected from it | ||||
// due to policy, allowing the node to function as a gateway for | // due to policy, allowing the node to function as a gateway for | ||||
// nodes hidden behind it. | // nodes hidden behind it. | ||||
// | // | ||||
// Never relay transactions that we would assign a non-zero DoS | // Never relay transactions that we would assign a non-zero DoS | ||||
// score for, as we expect peers to do the same with us in that | // score for, as we expect peers to do the same with us in that | ||||
// case. | // case. | ||||
int nDoS = 0; | int nDoS = 0; | ||||
if (!state.IsInvalid(nDoS) || nDoS == 0) { | if (!state.IsInvalid(nDoS) || nDoS == 0) { | ||||
LogPrintf("Force relaying tx %s from whitelisted peer=%d\n", | LogPrintf("Force relaying tx %s from whitelisted peer=%d\n", | ||||
tx.GetId().ToString(), pfrom->id); | tx.GetHash().ToString(), pfrom->id); | ||||
RelayTransaction(tx, connman); | RelayTransaction(tx, connman); | ||||
} else { | } else { | ||||
LogPrintf("Not relaying invalid transaction %s from " | LogPrintf("Not relaying invalid transaction %s from " | ||||
"whitelisted peer=%d (%s)\n", | "whitelisted peer=%d (%s)\n", | ||||
tx.GetId().ToString(), pfrom->id, | tx.GetHash().ToString(), pfrom->id, | ||||
FormatStateMessage(state)); | FormatStateMessage(state)); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
for (const CTransactionRef &removedTx : lRemovedTxn) { | for (const CTransactionRef &removedTx : lRemovedTxn) { | ||||
AddToCompactExtraTransactions(removedTx); | AddToCompactExtraTransactions(removedTx); | ||||
} | } | ||||
int nDoS = 0; | int nDoS = 0; | ||||
if (state.IsInvalid(nDoS)) { | if (state.IsInvalid(nDoS)) { | ||||
LogPrint("mempoolrej", "%s from peer=%d was not accepted: %s\n", | LogPrint("mempoolrej", "%s from peer=%d was not accepted: %s\n", | ||||
tx.GetId().ToString(), pfrom->id, | tx.GetHash().ToString(), pfrom->id, | ||||
FormatStateMessage(state)); | FormatStateMessage(state)); | ||||
// Never send AcceptToMemoryPool's internal codes over P2P. | // Never send AcceptToMemoryPool's internal codes over P2P. | ||||
if (state.GetRejectCode() < REJECT_INTERNAL) { | if (state.GetRejectCode() < REJECT_INTERNAL) { | ||||
connman.PushMessage( | connman.PushMessage( | ||||
pfrom, msgMaker.Make(NetMsgType::REJECT, strCommand, | pfrom, msgMaker.Make(NetMsgType::REJECT, strCommand, | ||||
uint8_t(state.GetRejectCode()), | uint8_t(state.GetRejectCode()), | ||||
state.GetRejectReason().substr( | state.GetRejectReason().substr( | ||||
0, MAX_REJECT_MESSAGE_LENGTH), | 0, MAX_REJECT_MESSAGE_LENGTH), | ||||
inv.hash)); | inv.hash)); | ||||
} | } | ||||
if (nDoS > 0) { | if (nDoS > 0) { | ||||
Misbehaving(pfrom, nDoS, state.GetRejectReason()); | Misbehaving(pfrom, nDoS, state.GetRejectReason()); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
// Ignore blocks received while importing | // Ignore blocks received while importing | ||||
else if (strCommand == NetMsgType::CMPCTBLOCK && !fImporting && !fReindex) { | else if (strCommand == NetMsgType::CMPCTBLOCK && !fImporting && !fReindex) { | ||||
CBlockHeaderAndShortTxIDs cmpctblock; | CBlockHeaderAndShortTxHashes cmpctblock; | ||||
vRecv >> cmpctblock; | vRecv >> cmpctblock; | ||||
{ | { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
if (mapBlockIndex.find(cmpctblock.header.hashPrevBlock) == | if (mapBlockIndex.find(cmpctblock.header.hashPrevBlock) == | ||||
mapBlockIndex.end()) { | mapBlockIndex.end()) { | ||||
// Doesn't connect (or is genesis), instead of DoSing in | // Doesn't connect (or is genesis), instead of DoSing in | ||||
Show All 20 Lines | else if (strCommand == NetMsgType::CMPCTBLOCK && !fImporting && !fReindex) { | ||||
Misbehaving(pfrom, nDoS, state.GetRejectReason()); | Misbehaving(pfrom, nDoS, state.GetRejectReason()); | ||||
} | } | ||||
LogPrintf("Peer %d sent us invalid header via cmpctblock\n", | LogPrintf("Peer %d sent us invalid header via cmpctblock\n", | ||||
pfrom->id); | pfrom->id); | ||||
return true; | return true; | ||||
} | } | ||||
} | } | ||||
// When we succeed in decoding a block's txids from a cmpctblock | // When we succeed in decoding a block's txhashes from a cmpctblock | ||||
// message we typically jump to the BLOCKTXN handling code, with a | // message we typically jump to the BLOCKTXN handling code, with a | ||||
// dummy (empty) BLOCKTXN message, to re-use the logic there in | // dummy (empty) BLOCKTXN message, to re-use the logic there in | ||||
// completing processing of the putative block (without cs_main). | // completing processing of the putative block (without cs_main). | ||||
bool fProcessBLOCKTXN = false; | bool fProcessBLOCKTXN = false; | ||||
CDataStream blockTxnMsg(SER_NETWORK, PROTOCOL_VERSION); | CDataStream blockTxnMsg(SER_NETWORK, PROTOCOL_VERSION); | ||||
// If we end up treating this as a plain headers message, call that as | // If we end up treating this as a plain headers message, call that as | ||||
// well | // well | ||||
▲ Show 20 Lines • Show All 908 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
class CompareInvMempoolOrder { | class CompareInvMempoolOrder { | ||||
CTxMemPool *mp; | CTxMemPool *mp; | ||||
public: | public: | ||||
CompareInvMempoolOrder(CTxMemPool *_mempool) { mp = _mempool; } | CompareInvMempoolOrder(CTxMemPool *_mempool) { mp = _mempool; } | ||||
bool operator()(std::set<uint256>::iterator a, | bool operator()(std::set<txhash_t>::iterator a, | ||||
std::set<uint256>::iterator b) { | std::set<txhash_t>::iterator b) { | ||||
/* As std::make_heap produces a max-heap, we want the entries with the | /* As std::make_heap produces a max-heap, we want the entries with the | ||||
* fewest ancestors/highest fee to sort later. */ | * fewest ancestors/highest fee to sort later. */ | ||||
return mp->CompareDepthAndScore(*b, *a); | return mp->CompareDepthAndScore(*b, *a); | ||||
} | } | ||||
}; | }; | ||||
bool SendMessages(const Config &config, CNode *pto, CConnman &connman, | bool SendMessages(const Config &config, CNode *pto, CConnman &connman, | ||||
const std::atomic<bool> &interruptMsgProc) { | const std::atomic<bool> &interruptMsgProc) { | ||||
▲ Show 20 Lines • Show All 217 Lines • ▼ Show 20 Lines | // | ||||
pto->id); | pto->id); | ||||
int nSendFlags = 0; | int nSendFlags = 0; | ||||
bool fGotBlockFromCache = false; | bool fGotBlockFromCache = false; | ||||
{ | { | ||||
LOCK(cs_most_recent_block); | LOCK(cs_most_recent_block); | ||||
if (most_recent_block_hash == pBestIndex->GetBlockHash()) { | if (most_recent_block_hash == pBestIndex->GetBlockHash()) { | ||||
CBlockHeaderAndShortTxIDs cmpctblock( | CBlockHeaderAndShortTxHashes cmpctblock( | ||||
*most_recent_block); | *most_recent_block); | ||||
connman.PushMessage( | connman.PushMessage( | ||||
pto, | pto, | ||||
msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, | msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, | ||||
cmpctblock)); | cmpctblock)); | ||||
fGotBlockFromCache = true; | fGotBlockFromCache = true; | ||||
} | } | ||||
} | } | ||||
if (!fGotBlockFromCache) { | if (!fGotBlockFromCache) { | ||||
CBlock block; | CBlock block; | ||||
bool ret = | bool ret = | ||||
ReadBlockFromDisk(block, pBestIndex, consensusParams); | ReadBlockFromDisk(block, pBestIndex, consensusParams); | ||||
assert(ret); | assert(ret); | ||||
CBlockHeaderAndShortTxIDs cmpctblock(block); | CBlockHeaderAndShortTxHashes cmpctblock(block); | ||||
connman.PushMessage( | connman.PushMessage( | ||||
pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, | pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, | ||||
cmpctblock)); | cmpctblock)); | ||||
} | } | ||||
state.pindexBestHeaderSent = pBestIndex; | state.pindexBestHeaderSent = pBestIndex; | ||||
} else if (state.fPreferHeaders) { | } else if (state.fPreferHeaders) { | ||||
if (vHeaders.size() > 1) { | if (vHeaders.size() > 1) { | ||||
LogPrint("net", | LogPrint("net", | ||||
▲ Show 20 Lines • Show All 90 Lines • ▼ Show 20 Lines | std::vector<CInv> vInv; | ||||
{ | { | ||||
LOCK(pto->cs_feeFilter); | LOCK(pto->cs_feeFilter); | ||||
filterrate = pto->minFeeFilter; | filterrate = pto->minFeeFilter; | ||||
} | } | ||||
LOCK(pto->cs_filter); | LOCK(pto->cs_filter); | ||||
for (const auto &txinfo : vtxinfo) { | for (const auto &txinfo : vtxinfo) { | ||||
const uint256 &txid = txinfo.tx->GetId(); | const txhash_t &txhash = txinfo.tx->GetHash(); | ||||
CInv inv(MSG_TX, txid); | CInv inv(MSG_TX, txhash); | ||||
pto->setInventoryTxToSend.erase(txid); | pto->setInventoryTxToSend.erase(txhash); | ||||
if (filterrate != 0) { | if (filterrate != 0) { | ||||
if (txinfo.feeRate.GetFeePerK() < filterrate) { | if (txinfo.feeRate.GetFeePerK() < filterrate) { | ||||
continue; | continue; | ||||
} | } | ||||
} | } | ||||
if (pto->pfilter) { | if (pto->pfilter) { | ||||
if (!pto->pfilter->IsRelevantAndUpdate(*txinfo.tx)) { | if (!pto->pfilter->IsRelevantAndUpdate(*txinfo.tx)) { | ||||
continue; | continue; | ||||
} | } | ||||
} | } | ||||
pto->filterInventoryKnown.insert(txid); | pto->filterInventoryKnown.insert(txhash); | ||||
vInv.push_back(inv); | vInv.push_back(inv); | ||||
if (vInv.size() == MAX_INV_SZ) { | if (vInv.size() == MAX_INV_SZ) { | ||||
connman.PushMessage(pto, | connman.PushMessage(pto, | ||||
msgMaker.Make(NetMsgType::INV, vInv)); | msgMaker.Make(NetMsgType::INV, vInv)); | ||||
vInv.clear(); | vInv.clear(); | ||||
} | } | ||||
} | } | ||||
pto->timeLastMempoolReq = GetTime(); | pto->timeLastMempoolReq = GetTime(); | ||||
} | } | ||||
// Determine transactions to relay | // Determine transactions to relay | ||||
if (fSendTrickle) { | if (fSendTrickle) { | ||||
// Produce a vector with all candidates for sending | // Produce a vector with all candidates for sending | ||||
std::vector<std::set<uint256>::iterator> vInvTx; | std::vector<std::set<txhash_t>::iterator> vInvTx; | ||||
vInvTx.reserve(pto->setInventoryTxToSend.size()); | vInvTx.reserve(pto->setInventoryTxToSend.size()); | ||||
for (std::set<uint256>::iterator it = | for (std::set<txhash_t>::iterator it = | ||||
pto->setInventoryTxToSend.begin(); | pto->setInventoryTxToSend.begin(); | ||||
it != pto->setInventoryTxToSend.end(); it++) { | it != pto->setInventoryTxToSend.end(); it++) { | ||||
vInvTx.push_back(it); | vInvTx.push_back(it); | ||||
} | } | ||||
Amount filterrate = 0; | Amount filterrate = 0; | ||||
{ | { | ||||
LOCK(pto->cs_feeFilter); | LOCK(pto->cs_feeFilter); | ||||
filterrate = pto->minFeeFilter; | filterrate = pto->minFeeFilter; | ||||
Show All 9 Lines | std::vector<CInv> vInv; | ||||
// shorter delays. | // shorter delays. | ||||
unsigned int nRelayedTransactions = 0; | unsigned int nRelayedTransactions = 0; | ||||
LOCK(pto->cs_filter); | LOCK(pto->cs_filter); | ||||
while (!vInvTx.empty() && | while (!vInvTx.empty() && | ||||
nRelayedTransactions < INVENTORY_BROADCAST_MAX) { | nRelayedTransactions < INVENTORY_BROADCAST_MAX) { | ||||
// Fetch the top element from the heap | // Fetch the top element from the heap | ||||
std::pop_heap(vInvTx.begin(), vInvTx.end(), | std::pop_heap(vInvTx.begin(), vInvTx.end(), | ||||
compareInvMempoolOrder); | compareInvMempoolOrder); | ||||
std::set<uint256>::iterator it = vInvTx.back(); | std::set<txhash_t>::iterator it = vInvTx.back(); | ||||
vInvTx.pop_back(); | vInvTx.pop_back(); | ||||
uint256 hash = *it; | txhash_t hash = *it; | ||||
// Remove it from the to-be-sent set | // Remove it from the to-be-sent set | ||||
pto->setInventoryTxToSend.erase(it); | pto->setInventoryTxToSend.erase(it); | ||||
// Check if not in the filter already | // Check if not in the filter already | ||||
if (pto->filterInventoryKnown.contains(hash)) { | if (pto->filterInventoryKnown.contains(hash)) { | ||||
continue; | continue; | ||||
} | } | ||||
// Not in the mempool anymore? don't bother sending it. | // Not in the mempool anymore? don't bother sending it. | ||||
auto txinfo = mempool.info(hash); | auto txinfo = mempool.info(hash); | ||||
▲ Show 20 Lines • Show All 195 Lines • Show Last 20 Lines |