Changeset View
Changeset View
Standalone View
Standalone View
src/net_processing.cpp
Show First 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | |||||
struct COrphanTx { | struct COrphanTx { | ||||
// When modifying, adapt the copy of this definition in tests/DoS_tests. | // When modifying, adapt the copy of this definition in tests/DoS_tests. | ||||
CTransactionRef tx; | CTransactionRef tx; | ||||
NodeId fromPeer; | NodeId fromPeer; | ||||
int64_t nTimeExpire; | int64_t nTimeExpire; | ||||
}; | }; | ||||
std::map<uint256, COrphanTx> mapOrphanTransactions GUARDED_BY(cs_main); | static CCriticalSection g_cs_orphans; | ||||
std::map<uint256, COrphanTx> mapOrphanTransactions GUARDED_BY(g_cs_orphans); | |||||
std::map<COutPoint, | std::map<COutPoint, | ||||
std::set<std::map<uint256, COrphanTx>::iterator, IteratorComparator>> | std::set<std::map<uint256, COrphanTx>::iterator, IteratorComparator>> | ||||
mapOrphanTransactionsByPrev GUARDED_BY(cs_main); | mapOrphanTransactionsByPrev GUARDED_BY(g_cs_orphans); | ||||
void EraseOrphansFor(NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(cs_main); | void EraseOrphansFor(NodeId peer); | ||||
static size_t vExtraTxnForCompactIt = 0; | static size_t vExtraTxnForCompactIt GUARDED_BY(g_cs_orphans) = 0; | ||||
static std::vector<std::pair<uint256, CTransactionRef>> | static std::vector<std::pair<uint256, CTransactionRef>> | ||||
vExtraTxnForCompact GUARDED_BY(cs_main); | vExtraTxnForCompact GUARDED_BY(g_cs_orphans); | ||||
// SHA256("main address relay")[0:8] | // SHA256("main address relay")[0:8] | ||||
static const uint64_t RANDOMIZER_ID_ADDRESS_RELAY = 0x3cac0035b5866b90ULL; | static const uint64_t RANDOMIZER_ID_ADDRESS_RELAY = 0x3cac0035b5866b90ULL; | ||||
/// Age after which a stale block will no longer be served if requested as | /// Age after which a stale block will no longer be served if requested as | ||||
/// protection against fingerprinting. Set to one month, denominated in seconds. | /// protection against fingerprinting. Set to one month, denominated in seconds. | ||||
static const int STALE_RELAY_AGE_LIMIT = 30 * 24 * 60 * 60; | static const int STALE_RELAY_AGE_LIMIT = 30 * 24 * 60 * 60; | ||||
▲ Show 20 Lines • Show All 58 Lines • ▼ Show 20 Lines | |||||
/** Number of peers from which we're downloading blocks. */ | /** Number of peers from which we're downloading blocks. */ | ||||
int nPeersWithValidatedDownloads GUARDED_BY(cs_main) = 0; | int nPeersWithValidatedDownloads GUARDED_BY(cs_main) = 0; | ||||
/** Number of outbound peers with m_chain_sync.m_protect. */ | /** Number of outbound peers with m_chain_sync.m_protect. */ | ||||
int g_outbound_peers_with_protect_from_disconnect = 0; | int g_outbound_peers_with_protect_from_disconnect = 0; | ||||
/** When our tip was last updated. */ | /** When our tip was last updated. */ | ||||
int64_t g_last_tip_update = 0; | std::atomic<int64_t> g_last_tip_update(0); | ||||
/** Relay map. */ | /** Relay map. */ | ||||
typedef std::map<uint256, CTransactionRef> MapRelay; | typedef std::map<uint256, CTransactionRef> MapRelay; | ||||
MapRelay mapRelay GUARDED_BY(cs_main); | MapRelay mapRelay GUARDED_BY(cs_main); | ||||
/** | /** | ||||
* Expiration-time ordered list of (expire time, relay map entry) pairs, | * Expiration-time ordered list of (expire time, relay map entry) pairs, | ||||
* protected by cs_main). | * protected by cs_main). | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 606 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
////////////////////////////////////////////////////////////////////////////// | ////////////////////////////////////////////////////////////////////////////// | ||||
// | // | ||||
// mapOrphanTransactions | // mapOrphanTransactions | ||||
// | // | ||||
static void AddToCompactExtraTransactions(const CTransactionRef &tx) | static void AddToCompactExtraTransactions(const CTransactionRef &tx) | ||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans) { | ||||
size_t max_extra_txn = gArgs.GetArg("-blockreconstructionextratxn", | size_t max_extra_txn = gArgs.GetArg("-blockreconstructionextratxn", | ||||
DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN); | DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN); | ||||
if (max_extra_txn <= 0) { | if (max_extra_txn <= 0) { | ||||
return; | return; | ||||
} | } | ||||
if (!vExtraTxnForCompact.size()) { | if (!vExtraTxnForCompact.size()) { | ||||
vExtraTxnForCompact.resize(max_extra_txn); | vExtraTxnForCompact.resize(max_extra_txn); | ||||
} | } | ||||
vExtraTxnForCompact[vExtraTxnForCompactIt] = | vExtraTxnForCompact[vExtraTxnForCompactIt] = | ||||
std::make_pair(tx->GetId(), tx); | std::make_pair(tx->GetId(), 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(g_cs_orphans) { | ||||
const uint256 &txid = tx->GetId(); | const uint256 &txid = tx->GetId(); | ||||
if (mapOrphanTransactions.count(txid)) { | if (mapOrphanTransactions.count(txid)) { | ||||
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 | ||||
Show All 18 Lines | bool AddOrphanTx(const CTransactionRef &tx, NodeId peer) | ||||
AddToCompactExtraTransactions(tx); | AddToCompactExtraTransactions(tx); | ||||
LogPrint(BCLog::MEMPOOL, "stored orphan tx %s (mapsz %u outsz %u)\n", | LogPrint(BCLog::MEMPOOL, "stored orphan tx %s (mapsz %u outsz %u)\n", | ||||
txid.ToString(), mapOrphanTransactions.size(), | txid.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(g_cs_orphans) { | ||||
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()) { | ||||
return 0; | return 0; | ||||
} | } | ||||
for (const CTxIn &txin : it->second.tx->vin) { | for (const CTxIn &txin : it->second.tx->vin) { | ||||
auto itPrev = mapOrphanTransactionsByPrev.find(txin.prevout); | auto itPrev = mapOrphanTransactionsByPrev.find(txin.prevout); | ||||
if (itPrev == mapOrphanTransactionsByPrev.end()) { | if (itPrev == mapOrphanTransactionsByPrev.end()) { | ||||
continue; | continue; | ||||
} | } | ||||
itPrev->second.erase(it); | itPrev->second.erase(it); | ||||
if (itPrev->second.empty()) { | if (itPrev->second.empty()) { | ||||
mapOrphanTransactionsByPrev.erase(itPrev); | mapOrphanTransactionsByPrev.erase(itPrev); | ||||
} | } | ||||
} | } | ||||
mapOrphanTransactions.erase(it); | mapOrphanTransactions.erase(it); | ||||
return 1; | return 1; | ||||
} | } | ||||
void EraseOrphansFor(NodeId peer) { | void EraseOrphansFor(NodeId peer) { | ||||
LOCK(g_cs_orphans); | |||||
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->GetId()); | ||||
} | } | ||||
} | } | ||||
if (nErased > 0) { | if (nErased > 0) { | ||||
LogPrint(BCLog::MEMPOOL, "Erased %d orphan tx from peer=%d\n", nErased, | LogPrint(BCLog::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) { | LOCK(g_cs_orphans); | ||||
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; | ||||
▲ Show 20 Lines • Show All 110 Lines • ▼ Show 20 Lines | scheduler.scheduleEvery( | ||||
return true; | return true; | ||||
}, | }, | ||||
EXTRA_PEER_CHECK_INTERVAL * 1000); | EXTRA_PEER_CHECK_INTERVAL * 1000); | ||||
} | } | ||||
void PeerLogicValidation::BlockConnected( | void PeerLogicValidation::BlockConnected( | ||||
const std::shared_ptr<const CBlock> &pblock, const CBlockIndex *pindex, | const std::shared_ptr<const CBlock> &pblock, const CBlockIndex *pindex, | ||||
const std::vector<CTransactionRef> &vtxConflicted) { | const std::vector<CTransactionRef> &vtxConflicted) { | ||||
LOCK(cs_main); | LOCK(g_cs_orphans); | ||||
std::vector<uint256> vOrphanErase; | std::vector<uint256> vOrphanErase; | ||||
for (const CTransactionRef &ptx : pblock->vtx) { | for (const CTransactionRef &ptx : pblock->vtx) { | ||||
const CTransaction &tx = *ptx; | const CTransaction &tx = *ptx; | ||||
// Which orphan pool entries must we evict? | // Which orphan pool entries must we evict? | ||||
for (size_t j = 0; j < tx.vin.size(); j++) { | for (size_t j = 0; j < tx.vin.size(); j++) { | ||||
▲ Show 20 Lines • Show All 174 Lines • ▼ Show 20 Lines | switch (inv.type) { | ||||
// 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(); | ||||
} | } | ||||
{ | |||||
LOCK(g_cs_orphans); | |||||
if (mapOrphanTransactions.count(inv.hash)) { | |||||
return true; | |||||
} | |||||
} | |||||
// Use pcoinsTip->HaveCoinInCache as a quick approximation to | // Use pcoinsTip->HaveCoinInCache as a quick approximation to | ||||
// exclude requesting or processing some txs which have already been | // exclude requesting or processing some txs which have already been | ||||
// included in a block. As this is best effort, we only check for | // included in a block. As this is best effort, we only check for | ||||
// output 0 and 1. This works well enough in practice and we get | // output 0 and 1. This works well enough in practice and we get | ||||
// diminishing returns with 2 onward. | // diminishing returns with 2 onward. | ||||
return recentRejects->contains(inv.hash) || | return recentRejects->contains(inv.hash) || | ||||
g_mempool.exists(inv.hash) || | g_mempool.exists(inv.hash) || | ||||
mapOrphanTransactions.count(inv.hash) || | |||||
pcoinsTip->HaveCoinInCache(COutPoint(inv.hash, 0)) || | pcoinsTip->HaveCoinInCache(COutPoint(inv.hash, 0)) || | ||||
pcoinsTip->HaveCoinInCache(COutPoint(inv.hash, 1)); | pcoinsTip->HaveCoinInCache(COutPoint(inv.hash, 1)); | ||||
} | } | ||||
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; | ||||
▲ Show 20 Lines • Show All 1,268 Lines • ▼ Show 20 Lines | else if (strCommand == NetMsgType::TX) { | ||||
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.GetId()); | ||||
pfrom->AddInventoryKnown(inv); | pfrom->AddInventoryKnown(inv); | ||||
LOCK(cs_main); | LOCK2(cs_main, g_cs_orphans); | ||||
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); | ||||
if (!AlreadyHave(inv) && | if (!AlreadyHave(inv) && | ||||
▲ Show 20 Lines • Show All 242 Lines • ▼ Show 20 Lines | else if (strCommand == NetMsgType::CMPCTBLOCK && !fImporting && !fReindex) { | ||||
bool fRevertToHeaderProcessing = false; | bool fRevertToHeaderProcessing = false; | ||||
// Keep a CBlock for "optimistic" compactblock reconstructions (see | // Keep a CBlock for "optimistic" compactblock reconstructions (see | ||||
// below) | // below) | ||||
std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>(); | std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>(); | ||||
bool fBlockReconstructed = false; | bool fBlockReconstructed = false; | ||||
{ | { | ||||
LOCK(cs_main); | LOCK2(cs_main, g_cs_orphans); | ||||
// If AcceptBlockHeader returned true, it set pindex | // If AcceptBlockHeader returned true, it set pindex | ||||
assert(pindex); | assert(pindex); | ||||
UpdateBlockAvailability(pfrom->GetId(), pindex->GetBlockHash()); | UpdateBlockAvailability(pfrom->GetId(), pindex->GetBlockHash()); | ||||
CNodeState *nodestate = State(pfrom->GetId()); | CNodeState *nodestate = State(pfrom->GetId()); | ||||
// If this was a new header with more work than our tip, update the | // If this was a new header with more work than our tip, update the | ||||
// peer's last block announcement time | // peer's last block announcement time | ||||
▲ Show 20 Lines • Show All 1,601 Lines • Show Last 20 Lines |