Changeset View
Changeset View
Standalone View
Standalone View
src/net_processing.cpp
- This file is larger than 256 KB, so syntax highlighting is disabled by default.
Show First 20 Lines • Show All 700 Lines • ▼ Show 20 Lines | private: | ||||
* punished if the block is invalid. | * punished if the block is invalid. | ||||
*/ | */ | ||||
std::map<BlockHash, std::pair<NodeId, bool>> | std::map<BlockHash, std::pair<NodeId, bool>> | ||||
mapBlockSource GUARDED_BY(cs_main); | mapBlockSource GUARDED_BY(cs_main); | ||||
/** Number of outbound peers with m_chain_sync.m_protect. */ | /** Number of outbound peers with m_chain_sync.m_protect. */ | ||||
int m_outbound_peers_with_protect_from_disconnect GUARDED_BY(cs_main) = 0; | int m_outbound_peers_with_protect_from_disconnect GUARDED_BY(cs_main) = 0; | ||||
/** | bool AlreadyHaveTx(const TxId &txid) EXCLUSIVE_LOCKS_REQUIRED(cs_main); | ||||
* Checks if address relay is permitted with peer. If needed, initializes | |||||
* the m_addr_known bloom filter and sets m_addr_relay_enabled to true. | |||||
* | |||||
* @return True if address relay is enabled with peer | |||||
* False if address relay is disallowed | |||||
*/ | |||||
bool SetupAddressRelay(CNode &node, Peer &peer); | |||||
}; | |||||
} // namespace | |||||
namespace { | |||||
/** | /** | ||||
* Filter for transactions that were recently rejected by AcceptToMemoryPool. | * Filter for transactions that were recently rejected by | ||||
* These are not rerequested until the chain tip changes, at which point the | * AcceptToMemoryPool. These are not rerequested until the chain tip | ||||
* entire filter is reset. | * changes, at which point the entire filter is reset. | ||||
* | * | ||||
* Without this filter we'd be re-requesting txs from each of our peers, | * Without this filter we'd be re-requesting txs from each of our peers, | ||||
* increasing bandwidth consumption considerably. For instance, with 100 peers, | * increasing bandwidth consumption considerably. For instance, with 100 | ||||
* half of which relay a tx we don't accept, that might be a 50x bandwidth | * peers, half of which relay a tx we don't accept, that might be a 50x | ||||
* increase. A flooding attacker attempting to roll-over the filter using | * bandwidth increase. A flooding attacker attempting to roll-over the | ||||
* minimum-sized, 60byte, transactions might manage to send 1000/sec if we have | * filter using minimum-sized, 60byte, transactions might manage to send | ||||
* fast peers, so we pick 120,000 to give our peers a two minute window to send | * 1000/sec if we have fast peers, so we pick 120,000 to give our peers a | ||||
* invs to us. | * two minute window to send invs to us. | ||||
* | * | ||||
* Decreasing the false positive rate is fairly cheap, so we pick one in a | * Decreasing the false positive rate is fairly cheap, so we pick one in a | ||||
* million to make it highly unlikely for users to have issues with this filter. | * million to make it highly unlikely for users to have issues with this | ||||
* filter. | |||||
* | * | ||||
* Memory used: 1.3 MB | * Memory used: 1.3 MB | ||||
*/ | */ | ||||
std::unique_ptr<CRollingBloomFilter> recentRejects GUARDED_BY(cs_main); | std::unique_ptr<CRollingBloomFilter> recentRejects GUARDED_BY(cs_main); | ||||
uint256 hashRecentRejectsChainTip GUARDED_BY(cs_main); | uint256 hashRecentRejectsChainTip GUARDED_BY(cs_main); | ||||
/** | /** | ||||
* Filter for transactions that have been recently confirmed. | |||||
* We use this to avoid requesting transactions that have already been | |||||
* confirmed. | |||||
*/ | |||||
Mutex m_recent_confirmed_transactions_mutex; | |||||
std::unique_ptr<CRollingBloomFilter> m_recent_confirmed_transactions | |||||
GUARDED_BY(m_recent_confirmed_transactions_mutex); | |||||
/** | |||||
* Checks if address relay is permitted with peer. If needed, initializes | |||||
* the m_addr_known bloom filter and sets m_addr_relay_enabled to true. | |||||
* | |||||
* @return True if address relay is enabled with peer | |||||
* False if address relay is disallowed | |||||
*/ | |||||
bool SetupAddressRelay(CNode &node, Peer &peer); | |||||
}; | |||||
} // namespace | |||||
namespace { | |||||
/** | |||||
* Filter for proofs that were recently rejected but not orphaned. | * Filter for proofs that were recently rejected but not orphaned. | ||||
* These are not rerequested until they are rolled out of the filter. | * These are not rerequested until they are rolled out of the filter. | ||||
* | * | ||||
* Without this filter we'd be re-requesting proofs from each of our peers, | * Without this filter we'd be re-requesting proofs from each of our peers, | ||||
* increasing bandwidth consumption considerably. | * increasing bandwidth consumption considerably. | ||||
* | * | ||||
* Decreasing the false positive rate is fairly cheap, so we pick one in a | * Decreasing the false positive rate is fairly cheap, so we pick one in a | ||||
* million to make it highly unlikely for users to have issues with this filter. | * million to make it highly unlikely for users to have issues with this filter. | ||||
*/ | */ | ||||
Mutex cs_rejectedProofs; | Mutex cs_rejectedProofs; | ||||
std::unique_ptr<CRollingBloomFilter> | std::unique_ptr<CRollingBloomFilter> | ||||
rejectedProofs GUARDED_BY(cs_rejectedProofs); | rejectedProofs GUARDED_BY(cs_rejectedProofs); | ||||
/** | /** | ||||
* Filter for transactions that have been recently confirmed. | |||||
* We use this to avoid requesting transactions that have already been | |||||
* confirmed. | |||||
*/ | |||||
Mutex g_cs_recent_confirmed_transactions; | |||||
std::unique_ptr<CRollingBloomFilter> g_recent_confirmed_transactions | |||||
GUARDED_BY(g_cs_recent_confirmed_transactions); | |||||
/** | |||||
* Blocks that are in flight, and that are in the queue to be downloaded. | * Blocks that are in flight, and that are in the queue to be downloaded. | ||||
*/ | */ | ||||
struct QueuedBlock { | struct QueuedBlock { | ||||
BlockHash hash; | BlockHash hash; | ||||
//! Optional. | //! Optional. | ||||
const CBlockIndex *pindex; | const CBlockIndex *pindex; | ||||
//! Whether this block has validated headers at the time of request. | //! Whether this block has validated headers at the time of request. | ||||
bool fValidatedHeaders; | bool fValidatedHeaders; | ||||
▲ Show 20 Lines • Show All 1,274 Lines • ▼ Show 20 Lines | PeerManagerImpl::PeerManagerImpl(const CChainParams &chainparams, | ||||
// Blocks don't typically have more than 4000 transactions, so this should | // Blocks don't typically have more than 4000 transactions, so this should | ||||
// be at least six blocks (~1 hr) worth of transactions that we can store. | // be at least six blocks (~1 hr) worth of transactions that we can store. | ||||
// If the number of transactions appearing in a block goes up, or if we are | // If the number of transactions appearing in a block goes up, or if we are | ||||
// seeing getdata requests more than an hour after initial announcement, we | // seeing getdata requests more than an hour after initial announcement, we | ||||
// can increase this number. | // can increase this number. | ||||
// The false positive rate of 1/1M should come out to less than 1 | // The false positive rate of 1/1M should come out to less than 1 | ||||
// transaction per day that would be inadvertently ignored (which is the | // transaction per day that would be inadvertently ignored (which is the | ||||
// same probability that we have in the reject filter). | // same probability that we have in the reject filter). | ||||
g_recent_confirmed_transactions.reset( | m_recent_confirmed_transactions.reset( | ||||
new CRollingBloomFilter(24000, 0.000001)); | new CRollingBloomFilter(24000, 0.000001)); | ||||
// Stale tip checking and peer eviction are on two different timers, but we | // Stale tip checking and peer eviction are on two different timers, but we | ||||
// don't want them to get out of sync due to drift in the scheduler, so we | // don't want them to get out of sync due to drift in the scheduler, so we | ||||
// combine them in one function and schedule at the quicker (peer-eviction) | // combine them in one function and schedule at the quicker (peer-eviction) | ||||
// timer. | // timer. | ||||
static_assert( | static_assert( | ||||
EXTRA_PEER_CHECK_INTERVAL < STALE_CHECK_INTERVAL, | EXTRA_PEER_CHECK_INTERVAL < STALE_CHECK_INTERVAL, | ||||
▲ Show 20 Lines • Show All 64 Lines • ▼ Show 20 Lines | const std::shared_ptr<const CBlock> &pblock, const CBlockIndex *pindex) { | ||||
LogPrint(BCLog::MEMPOOL, | LogPrint(BCLog::MEMPOOL, | ||||
"Erased %d orphan tx included or conflicted by block\n", | "Erased %d orphan tx included or conflicted by block\n", | ||||
nErased); | nErased); | ||||
} | } | ||||
g_last_tip_update = GetTime(); | g_last_tip_update = GetTime(); | ||||
} | } | ||||
{ | { | ||||
LOCK(g_cs_recent_confirmed_transactions); | LOCK(m_recent_confirmed_transactions_mutex); | ||||
for (const CTransactionRef &ptx : pblock->vtx) { | for (const CTransactionRef &ptx : pblock->vtx) { | ||||
g_recent_confirmed_transactions->insert(ptx->GetId()); | m_recent_confirmed_transactions->insert(ptx->GetId()); | ||||
} | } | ||||
} | } | ||||
{ | { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
for (const auto &ptx : pblock->vtx) { | for (const auto &ptx : pblock->vtx) { | ||||
m_txrequest.ForgetInvId(ptx->GetId()); | m_txrequest.ForgetInvId(ptx->GetId()); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
void PeerManagerImpl::BlockDisconnected( | void PeerManagerImpl::BlockDisconnected( | ||||
const std::shared_ptr<const CBlock> &block, const CBlockIndex *pindex) { | const std::shared_ptr<const CBlock> &block, const CBlockIndex *pindex) { | ||||
// To avoid relay problems with transactions that were previously | // To avoid relay problems with transactions that were previously | ||||
// confirmed, clear our filter of recently confirmed transactions whenever | // confirmed, clear our filter of recently confirmed transactions whenever | ||||
// there's a reorg. | // there's a reorg. | ||||
// This means that in a 1-block reorg (where 1 block is disconnected and | // This means that in a 1-block reorg (where 1 block is disconnected and | ||||
// then another block reconnected), our filter will drop to having only one | // then another block reconnected), our filter will drop to having only one | ||||
// block's worth of transactions in it, but that should be fine, since | // block's worth of transactions in it, but that should be fine, since | ||||
// presumably the most common case of relaying a confirmed transaction | // presumably the most common case of relaying a confirmed transaction | ||||
// should be just after a new block containing it is found. | // should be just after a new block containing it is found. | ||||
LOCK(g_cs_recent_confirmed_transactions); | LOCK(m_recent_confirmed_transactions_mutex); | ||||
g_recent_confirmed_transactions->reset(); | m_recent_confirmed_transactions->reset(); | ||||
} | } | ||||
// All of the following cache a recent block, and are protected by | // All of the following cache a recent block, and are protected by | ||||
// cs_most_recent_block | // cs_most_recent_block | ||||
static RecursiveMutex cs_most_recent_block; | static RecursiveMutex cs_most_recent_block; | ||||
static std::shared_ptr<const CBlock> | static std::shared_ptr<const CBlock> | ||||
most_recent_block GUARDED_BY(cs_most_recent_block); | most_recent_block GUARDED_BY(cs_most_recent_block); | ||||
static std::shared_ptr<const CBlockHeaderAndShortTxIDs> | static std::shared_ptr<const CBlockHeaderAndShortTxIDs> | ||||
▲ Show 20 Lines • Show All 135 Lines • ▼ Show 20 Lines | void PeerManagerImpl::BlockChecked(const CBlock &block, | ||||
} | } | ||||
} | } | ||||
////////////////////////////////////////////////////////////////////////////// | ////////////////////////////////////////////////////////////////////////////// | ||||
// | // | ||||
// Messages | // Messages | ||||
// | // | ||||
static bool AlreadyHaveTx(const TxId &txid, const CTxMemPool &mempool) | bool PeerManagerImpl::AlreadyHaveTx(const TxId &txid) | ||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | ||||
assert(recentRejects); | assert(recentRejects); | ||||
if (::ChainActive().Tip()->GetBlockHash() != hashRecentRejectsChainTip) { | if (::ChainActive().Tip()->GetBlockHash() != 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(); | ||||
} | } | ||||
{ | { | ||||
LOCK(g_cs_orphans); | LOCK(g_cs_orphans); | ||||
if (mapOrphanTransactions.count(txid)) { | if (mapOrphanTransactions.count(txid)) { | ||||
return true; | return true; | ||||
} | } | ||||
} | } | ||||
{ | { | ||||
LOCK(g_cs_recent_confirmed_transactions); | LOCK(m_recent_confirmed_transactions_mutex); | ||||
if (g_recent_confirmed_transactions->contains(txid)) { | if (m_recent_confirmed_transactions->contains(txid)) { | ||||
return true; | return true; | ||||
} | } | ||||
} | } | ||||
return recentRejects->contains(txid) || mempool.exists(txid); | return recentRejects->contains(txid) || m_mempool.exists(txid); | ||||
} | } | ||||
static bool AlreadyHaveBlock(const BlockHash &block_hash) | static bool AlreadyHaveBlock(const BlockHash &block_hash) | ||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | ||||
return g_chainman.m_blockman.LookupBlockIndex(block_hash) != nullptr; | return g_chainman.m_blockman.LookupBlockIndex(block_hash) != nullptr; | ||||
} | } | ||||
static bool AlreadyHaveProof(const avalanche::ProofId &proofid) { | static bool AlreadyHaveProof(const avalanche::ProofId &proofid) { | ||||
▲ Show 20 Lines • Show All 1,715 Lines • ▼ Show 20 Lines | if (msg_type == NetMsgType::INV) { | ||||
preferred); | preferred); | ||||
} | } | ||||
continue; | continue; | ||||
} | } | ||||
if (inv.IsMsgTx()) { | if (inv.IsMsgTx()) { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
const TxId txid(inv.hash); | const TxId txid(inv.hash); | ||||
const bool fAlreadyHave = AlreadyHaveTx(txid, m_mempool); | const bool fAlreadyHave = AlreadyHaveTx(txid); | ||||
logInv(inv, fAlreadyHave); | logInv(inv, fAlreadyHave); | ||||
pfrom.AddKnownTx(txid); | pfrom.AddKnownTx(txid); | ||||
if (fBlocksOnly) { | if (fBlocksOnly) { | ||||
LogPrint(BCLog::NET, | LogPrint(BCLog::NET, | ||||
"transaction (%s) inv sent in violation of " | "transaction (%s) inv sent in violation of " | ||||
"protocol, disconnecting peer=%d\n", | "protocol, disconnecting peer=%d\n", | ||||
txid.ToString(), pfrom.GetId()); | txid.ToString(), pfrom.GetId()); | ||||
▲ Show 20 Lines • Show All 303 Lines • ▼ Show 20 Lines | if (msg_type == NetMsgType::TX) { | ||||
const CTransaction &tx = *ptx; | const CTransaction &tx = *ptx; | ||||
const TxId &txid = tx.GetId(); | const TxId &txid = tx.GetId(); | ||||
pfrom.AddKnownTx(txid); | pfrom.AddKnownTx(txid); | ||||
LOCK2(cs_main, g_cs_orphans); | LOCK2(cs_main, g_cs_orphans); | ||||
m_txrequest.ReceivedResponse(pfrom.GetId(), txid); | m_txrequest.ReceivedResponse(pfrom.GetId(), txid); | ||||
if (AlreadyHaveTx(txid, m_mempool)) { | if (AlreadyHaveTx(txid)) { | ||||
if (pfrom.HasPermission(PF_FORCERELAY)) { | if (pfrom.HasPermission(PF_FORCERELAY)) { | ||||
// Always relay transactions received from peers with | // Always relay transactions received from peers with | ||||
// forcerelay permission, even if they were already in the | // forcerelay permission, even if they were already in the | ||||
// mempool, allowing the node to function as a gateway for | // mempool, allowing the node to function as a gateway for | ||||
// nodes hidden behind it. | // nodes hidden behind it. | ||||
if (!m_mempool.exists(tx.GetId())) { | if (!m_mempool.exists(tx.GetId())) { | ||||
LogPrintf("Not relaying non-mempool transaction %s from " | LogPrintf("Not relaying non-mempool transaction %s from " | ||||
"forcerelay peer=%d\n", | "forcerelay peer=%d\n", | ||||
▲ Show 20 Lines • Show All 61 Lines • ▼ Show 20 Lines | if (msg_type == NetMsgType::TX) { | ||||
} | } | ||||
} | } | ||||
if (!fRejectedParents) { | if (!fRejectedParents) { | ||||
const auto current_time = GetTime<std::chrono::microseconds>(); | const auto current_time = GetTime<std::chrono::microseconds>(); | ||||
for (const TxId &parent_txid : unique_parents) { | for (const TxId &parent_txid : unique_parents) { | ||||
// FIXME: MSG_TX should use a TxHash, not a TxId. | // FIXME: MSG_TX should use a TxHash, not a TxId. | ||||
pfrom.AddKnownTx(parent_txid); | pfrom.AddKnownTx(parent_txid); | ||||
if (!AlreadyHaveTx(parent_txid, m_mempool)) { | if (!AlreadyHaveTx(parent_txid)) { | ||||
AddTxAnnouncement(pfrom, parent_txid, current_time); | AddTxAnnouncement(pfrom, parent_txid, current_time); | ||||
} | } | ||||
} | } | ||||
AddOrphanTx(ptx, pfrom.GetId()); | AddOrphanTx(ptx, pfrom.GetId()); | ||||
// Once added to the orphan pool, a tx is considered | // Once added to the orphan pool, a tx is considered | ||||
// AlreadyHave, and we shouldn't request it anymore. | // AlreadyHave, and we shouldn't request it anymore. | ||||
m_txrequest.ForgetInvId(tx.GetId()); | m_txrequest.ForgetInvId(tx.GetId()); | ||||
▲ Show 20 Lines • Show All 2,437 Lines • ▼ Show 20 Lines | // | ||||
std::vector<std::pair<NodeId, TxId>> expired; | std::vector<std::pair<NodeId, TxId>> expired; | ||||
auto requestable = | auto requestable = | ||||
m_txrequest.GetRequestable(pto->GetId(), current_time, &expired); | m_txrequest.GetRequestable(pto->GetId(), current_time, &expired); | ||||
for (const auto &entry : expired) { | for (const auto &entry : expired) { | ||||
LogPrint(BCLog::NET, "timeout of inflight tx %s from peer=%d\n", | LogPrint(BCLog::NET, "timeout of inflight tx %s from peer=%d\n", | ||||
entry.second.ToString(), entry.first); | entry.second.ToString(), entry.first); | ||||
} | } | ||||
for (const TxId &txid : requestable) { | for (const TxId &txid : requestable) { | ||||
if (!AlreadyHaveTx(txid, m_mempool)) { | if (!AlreadyHaveTx(txid)) { | ||||
addGetDataAndMaybeFlush(MSG_TX, txid); | addGetDataAndMaybeFlush(MSG_TX, txid); | ||||
m_txrequest.RequestedData( | m_txrequest.RequestedData( | ||||
pto->GetId(), txid, | pto->GetId(), txid, | ||||
current_time + TX_REQUEST_PARAMS.getdata_interval); | current_time + TX_REQUEST_PARAMS.getdata_interval); | ||||
} else { | } else { | ||||
// We have already seen this transaction, no need to download. | // We have already seen this transaction, no need to download. | ||||
// This is just a belt-and-suspenders, as this should already be | // This is just a belt-and-suspenders, as this should already be | ||||
// called whenever a transaction becomes AlreadyHaveTx(). | // called whenever a transaction becomes AlreadyHaveTx(). | ||||
▲ Show 20 Lines • Show All 80 Lines • Show Last 20 Lines |