aboutsummaryrefslogtreecommitdiff
path: root/src/net_processing.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/net_processing.cpp')
-rw-r--r--src/net_processing.cpp679
1 files changed, 365 insertions, 314 deletions
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 5f1e7318f..60bdfbe9f 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -143,6 +143,8 @@ static constexpr unsigned int MAX_FEEFILTER_CHANGE_DELAY = 5 * 60;
static constexpr uint32_t MAX_GETCFILTERS_SIZE = 1000;
/** Maximum number of cf hashes that may be requested with one getcfheaders. See BIP 157. */
static constexpr uint32_t MAX_GETCFHEADERS_SIZE = 2000;
+/** the maximum percentage of addresses from our addrman to return in response to a getaddr message. */
+static constexpr size_t MAX_PCT_ADDR_TO_SEND = 23;
struct COrphanTx {
// When modifying, adapt the copy of this definition in tests/DoS_tests.
@@ -157,9 +159,6 @@ std::map<uint256, std::map<uint256, COrphanTx>::iterator> g_orphans_by_wtxid GUA
void EraseOrphansFor(NodeId peer);
-/** Increase a node's misbehavior score. */
-void Misbehaving(NodeId nodeid, int howmuch, const std::string& message="") EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-
// Internal stuff
namespace {
/** Number of nodes with fSyncStarted. */
@@ -190,7 +189,7 @@ namespace {
* million to make it highly unlikely for users to have issues with this
* filter.
*
- * We only need to add wtxids to this filter. For non-segwit
+ * We typically only add wtxids to this filter. For non-segwit
* transactions, the txid == wtxid, so this only prevents us from
* re-downloading non-segwit transactions when communicating with
* non-wtxidrelay peers -- which is important for avoiding malleation
@@ -199,6 +198,12 @@ namespace {
* the reject filter store wtxids is exactly what we want to avoid
* redownload of a rejected transaction.
*
+ * In cases where we can tell that a segwit transaction will fail
+ * validation no matter the witness, we may add the txid of such
+ * transaction to the filter as well. This can be helpful when
+ * communicating with txid-relay peers or if we were to otherwise fetch a
+ * transaction via txid (eg in our orphan handling).
+ *
* Memory used: 1.3 MB
*/
std::unique_ptr<CRollingBloomFilter> recentRejects GUARDED_BY(cs_main);
@@ -239,7 +244,7 @@ namespace {
/** When our tip was last updated. */
std::atomic<int64_t> g_last_tip_update(0);
- /** Relay map */
+ /** Relay map (txid or wtxid -> CTransactionRef) */
typedef std::map<uint256, CTransactionRef> MapRelay;
MapRelay mapRelay GUARDED_BY(cs_main);
/** Expiration-time ordered list of (expire time, relay map entry) pairs. */
@@ -401,7 +406,7 @@ struct CNodeState {
/* Track when to attempt download of announced transactions (process
* time in micros -> txid)
*/
- std::multimap<std::chrono::microseconds, uint256> m_tx_process_time;
+ std::multimap<std::chrono::microseconds, GenTxid> m_tx_process_time;
//! Store all the transactions a peer has recently announced
std::set<uint256> m_tx_announced;
@@ -476,7 +481,7 @@ static void UpdatePreferredDownload(const CNode& node, CNodeState* state) EXCLUS
nPreferredDownload -= state->fPreferredDownload;
// Whether this node should be marked as a preferred download node.
- state->fPreferredDownload = (!node.fInbound || node.HasPermission(PF_NOBAN)) && !node.fOneShot && !node.fClient;
+ state->fPreferredDownload = (!node.IsInboundConn() || node.HasPermission(PF_NOBAN)) && !node.IsAddrFetchConn() && !node.fClient;
nPreferredDownload += state->fPreferredDownload;
}
@@ -754,34 +759,34 @@ static void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vec
}
}
-void EraseTxRequest(const uint256& txid) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+void EraseTxRequest(const GenTxid& gtxid) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
- g_already_asked_for.erase(txid);
+ g_already_asked_for.erase(gtxid.GetHash());
}
-std::chrono::microseconds GetTxRequestTime(const uint256& txid) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+std::chrono::microseconds GetTxRequestTime(const GenTxid& gtxid) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
- auto it = g_already_asked_for.find(txid);
+ auto it = g_already_asked_for.find(gtxid.GetHash());
if (it != g_already_asked_for.end()) {
return it->second;
}
return {};
}
-void UpdateTxRequestTime(const uint256& txid, std::chrono::microseconds request_time) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+void UpdateTxRequestTime(const GenTxid& gtxid, std::chrono::microseconds request_time) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
- auto it = g_already_asked_for.find(txid);
+ auto it = g_already_asked_for.find(gtxid.GetHash());
if (it == g_already_asked_for.end()) {
- g_already_asked_for.insert(std::make_pair(txid, request_time));
+ g_already_asked_for.insert(std::make_pair(gtxid.GetHash(), request_time));
} else {
g_already_asked_for.update(it, request_time);
}
}
-std::chrono::microseconds CalculateTxGetDataTime(const uint256& txid, std::chrono::microseconds current_time, bool use_inbound_delay, bool use_txid_delay) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+std::chrono::microseconds CalculateTxGetDataTime(const GenTxid& gtxid, std::chrono::microseconds current_time, bool use_inbound_delay, bool use_txid_delay) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
std::chrono::microseconds process_time;
- const auto last_request_time = GetTxRequestTime(txid);
+ const auto last_request_time = GetTxRequestTime(gtxid);
// First time requesting this tx
if (last_request_time.count() == 0) {
process_time = current_time;
@@ -800,23 +805,23 @@ std::chrono::microseconds CalculateTxGetDataTime(const uint256& txid, std::chron
return process_time;
}
-void RequestTx(CNodeState* state, const uint256& txid, std::chrono::microseconds current_time) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+void RequestTx(CNodeState* state, const GenTxid& gtxid, std::chrono::microseconds current_time) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
CNodeState::TxDownloadState& peer_download_state = state->m_tx_download;
if (peer_download_state.m_tx_announced.size() >= MAX_PEER_TX_ANNOUNCEMENTS ||
peer_download_state.m_tx_process_time.size() >= MAX_PEER_TX_ANNOUNCEMENTS ||
- peer_download_state.m_tx_announced.count(txid)) {
+ peer_download_state.m_tx_announced.count(gtxid.GetHash())) {
// Too many queued announcements from this peer, or we already have
// this announcement
return;
}
- peer_download_state.m_tx_announced.insert(txid);
+ peer_download_state.m_tx_announced.insert(gtxid.GetHash());
// Calculate the time to try requesting this transaction. Use
// fPreferredDownload as a proxy for outbound peers.
- const auto process_time = CalculateTxGetDataTime(txid, current_time, !state->fPreferredDownload, !state->m_wtxid_relay && g_wtxid_relay_peers > 0);
+ const auto process_time = CalculateTxGetDataTime(gtxid, current_time, !state->fPreferredDownload, !state->m_wtxid_relay && g_wtxid_relay_peers > 0);
- peer_download_state.m_tx_process_time.emplace(process_time, txid);
+ peer_download_state.m_tx_process_time.emplace(process_time, gtxid);
}
} // namespace
@@ -830,23 +835,16 @@ void UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds)
if (state) state->m_last_block_announcement = time_in_seconds;
}
-// Returns true for outbound peers, excluding manual connections, feelers, and
-// one-shots.
-static bool IsOutboundDisconnectionCandidate(const CNode& node)
-{
- return !(node.fInbound || node.m_manual_connection || node.fFeeler || node.fOneShot);
-}
-
void PeerLogicValidation::InitializeNode(CNode *pnode) {
CAddress addr = pnode->addr;
std::string addrName = pnode->GetAddrName();
NodeId nodeid = pnode->GetId();
{
LOCK(cs_main);
- mapNodeState.emplace_hint(mapNodeState.end(), std::piecewise_construct, std::forward_as_tuple(nodeid), std::forward_as_tuple(addr, std::move(addrName), pnode->fInbound, pnode->m_manual_connection));
+ mapNodeState.emplace_hint(mapNodeState.end(), std::piecewise_construct, std::forward_as_tuple(nodeid), std::forward_as_tuple(addr, std::move(addrName), pnode->IsInboundConn(), pnode->IsManualConn()));
}
- if(!pnode->fInbound)
- PushNodeVersion(*pnode, *connman, GetTime());
+ if(!pnode->IsInboundConn())
+ PushNodeVersion(*pnode, m_connman, GetTime());
}
void PeerLogicValidation::ReattemptInitialBroadcast(CScheduler& scheduler) const
@@ -857,7 +855,7 @@ void PeerLogicValidation::ReattemptInitialBroadcast(CScheduler& scheduler) const
// Sanity check: all unbroadcast txns should exist in the mempool
if (m_mempool.exists(elem.first)) {
LOCK(cs_main);
- RelayTransaction(elem.first, elem.second, *connman);
+ RelayTransaction(elem.first, elem.second, m_connman);
} else {
m_mempool.RemoveUnbroadcastTx(elem.first, true);
}
@@ -1062,23 +1060,22 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans)
* Increment peer's misbehavior score. If the new value >= DISCOURAGEMENT_THRESHOLD, mark the node
* to be discouraged, meaning the peer might be disconnected and added to the discouragement filter.
*/
-void Misbehaving(NodeId pnode, int howmuch, const std::string& message) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+void Misbehaving(const NodeId pnode, const int howmuch, const std::string& message) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
- if (howmuch == 0)
- return;
+ assert(howmuch > 0);
- CNodeState *state = State(pnode);
- if (state == nullptr)
- return;
+ CNodeState* const state = State(pnode);
+ if (state == nullptr) return;
state->nMisbehavior += howmuch;
- std::string message_prefixed = message.empty() ? "" : (": " + message);
+ const std::string message_prefixed = message.empty() ? "" : (": " + message);
if (state->nMisbehavior >= DISCOURAGEMENT_THRESHOLD && state->nMisbehavior - howmuch < DISCOURAGEMENT_THRESHOLD)
{
- LogPrint(BCLog::NET, "%s: %s peer=%d (%d -> %d) DISCOURAGE THRESHOLD EXCEEDED%s\n", __func__, state->name, pnode, state->nMisbehavior-howmuch, state->nMisbehavior, message_prefixed);
+ LogPrint(BCLog::NET, "Misbehaving: peer=%d (%d -> %d) DISCOURAGE THRESHOLD EXCEEDED%s\n", pnode, state->nMisbehavior - howmuch, state->nMisbehavior, message_prefixed);
state->m_should_discourage = true;
- } else
- LogPrint(BCLog::NET, "%s: %s peer=%d (%d -> %d)%s\n", __func__, state->name, pnode, state->nMisbehavior-howmuch, state->nMisbehavior, message_prefixed);
+ } else {
+ LogPrint(BCLog::NET, "Misbehaving: peer=%d (%d -> %d)%s\n", pnode, state->nMisbehavior - howmuch, state->nMisbehavior, message_prefixed);
+ }
}
/**
@@ -1165,6 +1162,7 @@ static bool MaybePunishNodeForTx(NodeId nodeid, const TxValidationState& state,
}
// Conflicting (but not necessarily invalid) data or different policy:
case TxValidationResult::TX_RECENT_CONSENSUS_CHANGE:
+ case TxValidationResult::TX_INPUTS_NOT_STANDARD:
case TxValidationResult::TX_NOT_STANDARD:
case TxValidationResult::TX_MISSING_INPUTS:
case TxValidationResult::TX_PREMATURE_SPEND:
@@ -1199,8 +1197,8 @@ static bool BlockRequestAllowed(const CBlockIndex* pindex, const Consensus::Para
(GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, consensusParams) < STALE_RELAY_AGE_LIMIT);
}
-PeerLogicValidation::PeerLogicValidation(CConnman* connmanIn, BanMan* banman, CScheduler& scheduler, ChainstateManager& chainman, CTxMemPool& pool)
- : connman(connmanIn),
+PeerLogicValidation::PeerLogicValidation(CConnman& connman, BanMan* banman, CScheduler& scheduler, ChainstateManager& chainman, CTxMemPool& pool)
+ : m_connman(connman),
m_banman(banman),
m_chainman(chainman),
m_mempool(pool),
@@ -1328,7 +1326,7 @@ void PeerLogicValidation::NewPoWValidBlock(const CBlockIndex *pindex, const std:
fWitnessesPresentInMostRecentCompactBlock = fWitnessEnabled;
}
- connman->ForEachNode([this, &pcmpctblock, pindex, &msgMaker, fWitnessEnabled, &hashBlock](CNode* pnode) {
+ m_connman.ForEachNode([this, &pcmpctblock, pindex, &msgMaker, fWitnessEnabled, &hashBlock](CNode* pnode) {
AssertLockHeld(cs_main);
// TODO: Avoid the repeated-serialization here
@@ -1343,7 +1341,7 @@ void PeerLogicValidation::NewPoWValidBlock(const CBlockIndex *pindex, const std:
LogPrint(BCLog::NET, "%s sending header-and-ids %s to peer=%d\n", "PeerLogicValidation::NewPoWValidBlock",
hashBlock.ToString(), pnode->GetId());
- connman->PushMessage(pnode, msgMaker.Make(NetMsgType::CMPCTBLOCK, *pcmpctblock));
+ m_connman.PushMessage(pnode, msgMaker.Make(NetMsgType::CMPCTBLOCK, *pcmpctblock));
state.pindexBestHeaderSent = pindex;
}
});
@@ -1355,7 +1353,7 @@ void PeerLogicValidation::NewPoWValidBlock(const CBlockIndex *pindex, const std:
*/
void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {
const int nNewHeight = pindexNew->nHeight;
- connman->SetBestHeight(nNewHeight);
+ m_connman.SetBestHeight(nNewHeight);
SetServiceFlagsIBDCache(!fInitialDownload);
if (!fInitialDownload) {
@@ -1372,7 +1370,7 @@ void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CB
}
}
// Relay inventory, but don't relay old inventory during initial block download.
- connman->ForEachNode([nNewHeight, &vHashes](CNode* pnode) {
+ m_connman.ForEachNode([nNewHeight, &vHashes](CNode* pnode) {
LOCK(pnode->cs_inventory);
if (nNewHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : 0)) {
for (const uint256& hash : reverse_iterate(vHashes)) {
@@ -1380,7 +1378,7 @@ void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CB
}
}
});
- connman->WakeMessageHandler();
+ m_connman.WakeMessageHandler();
}
}
@@ -1411,7 +1409,7 @@ void PeerLogicValidation::BlockChecked(const CBlock& block, const BlockValidatio
!::ChainstateActive().IsInitialBlockDownload() &&
mapBlocksInFlight.count(hash) == mapBlocksInFlight.size()) {
if (it != mapBlockSource.end()) {
- MaybeSetPeerAsAnnouncingHeaderAndIDs(it->second.first, *connman);
+ MaybeSetPeerAsAnnouncingHeaderAndIDs(it->second.first, m_connman);
}
}
if (it != mapBlockSource.end())
@@ -1445,9 +1443,9 @@ bool static AlreadyHave(const CInv& inv, const CTxMemPool& mempool) EXCLUSIVE_LO
{
LOCK(g_cs_orphans);
- if (inv.type != MSG_WTX && mapOrphanTransactions.count(inv.hash)) {
+ if (!inv.IsMsgWtx() && mapOrphanTransactions.count(inv.hash)) {
return true;
- } else if (inv.type == MSG_WTX && g_orphans_by_wtxid.count(inv.hash)) {
+ } else if (inv.IsMsgWtx() && g_orphans_by_wtxid.count(inv.hash)) {
return true;
}
}
@@ -1457,8 +1455,7 @@ bool static AlreadyHave(const CInv& inv, const CTxMemPool& mempool) EXCLUSIVE_LO
if (g_recent_confirmed_transactions->contains(inv.hash)) return true;
}
- const bool by_wtxid = (inv.type == MSG_WTX);
- return recentRejects->contains(inv.hash) || mempool.exists(inv.hash, by_wtxid);
+ return recentRejects->contains(inv.hash) || mempool.exists(ToGenTxid(inv));
}
case MSG_BLOCK:
case MSG_WITNESS_BLOCK:
@@ -1676,9 +1673,9 @@ void static ProcessGetBlockData(CNode& pfrom, const CChainParams& chainparams, c
}
//! Determine whether or not a peer can request a transaction, and return it (or nullptr if not found or not allowed).
-CTransactionRef static FindTxForGetData(const CNode& peer, const uint256& txid_or_wtxid, bool use_wtxid, const std::chrono::seconds mempool_req, const std::chrono::seconds now) LOCKS_EXCLUDED(cs_main)
+CTransactionRef static FindTxForGetData(const CNode& peer, const GenTxid& gtxid, const std::chrono::seconds mempool_req, const std::chrono::seconds now) LOCKS_EXCLUDED(cs_main)
{
- auto txinfo = mempool.info(txid_or_wtxid, use_wtxid);
+ auto txinfo = mempool.info(gtxid);
if (txinfo.tx) {
// If a TX could have been INVed in reply to a MEMPOOL request,
// or is older than UNCONDITIONAL_RELAY_DELAY, permit the request
@@ -1691,11 +1688,11 @@ CTransactionRef static FindTxForGetData(const CNode& peer, const uint256& txid_o
{
LOCK(cs_main);
// Otherwise, the transaction must have been announced recently.
- if (State(peer.GetId())->m_recently_announced_invs.contains(txid_or_wtxid)) {
+ if (State(peer.GetId())->m_recently_announced_invs.contains(gtxid.GetHash())) {
// If it was, it can be relayed from either the mempool...
if (txinfo.tx) return std::move(txinfo.tx);
// ... or the relay pool.
- auto mi = mapRelay.find(txid_or_wtxid);
+ auto mi = mapRelay.find(gtxid.GetHash());
if (mi != mapRelay.end()) return mi->second;
}
}
@@ -1719,7 +1716,7 @@ void static ProcessGetData(CNode& pfrom, const CChainParams& chainparams, CConnm
// Process as many TX items from the front of the getdata queue as
// possible, since they're common and it's efficient to batch process
// them.
- while (it != pfrom.vRecvGetData.end() && (it->type == MSG_TX || it->type == MSG_WITNESS_TX || it->type == MSG_WTX)) {
+ while (it != pfrom.vRecvGetData.end() && it->IsGenTxMsg()) {
if (interruptMsgProc) return;
// The send buffer provides backpressure. If there's no space in
// the buffer, pause processing until the next call.
@@ -1732,23 +1729,34 @@ void static ProcessGetData(CNode& pfrom, const CChainParams& chainparams, CConnm
continue;
}
- CTransactionRef tx = FindTxForGetData(pfrom, inv.hash, inv.type == MSG_WTX, mempool_req, now);
+ CTransactionRef tx = FindTxForGetData(pfrom, ToGenTxid(inv), mempool_req, now);
if (tx) {
// WTX and WITNESS_TX imply we serialize with witness
- int nSendFlags = (inv.type == MSG_TX ? SERIALIZE_TRANSACTION_NO_WITNESS : 0);
+ int nSendFlags = (inv.IsMsgTx() ? SERIALIZE_TRANSACTION_NO_WITNESS : 0);
connman.PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *tx));
mempool.RemoveUnbroadcastTx(tx->GetHash());
// As we're going to send tx, make sure its unconfirmed parents are made requestable.
- for (const auto& txin : tx->vin) {
- auto txinfo = mempool.info(txin.prevout.hash);
- if (txinfo.tx && txinfo.m_time > now - UNCONDITIONAL_RELAY_DELAY) {
- // Relaying a transaction with a recent but unconfirmed parent.
- if (WITH_LOCK(pfrom.m_tx_relay->cs_tx_inventory, return !pfrom.m_tx_relay->filterInventoryKnown.contains(txin.prevout.hash))) {
- LOCK(cs_main);
- State(pfrom.GetId())->m_recently_announced_invs.insert(txin.prevout.hash);
+ std::vector<uint256> parent_ids_to_add;
+ {
+ LOCK(mempool.cs);
+ auto txiter = mempool.GetIter(tx->GetHash());
+ if (txiter) {
+ const CTxMemPool::setEntries& parents = mempool.GetMemPoolParents(*txiter);
+ parent_ids_to_add.reserve(parents.size());
+ for (CTxMemPool::txiter parent_iter : parents) {
+ if (parent_iter->GetTime() > now - UNCONDITIONAL_RELAY_DELAY) {
+ parent_ids_to_add.push_back(parent_iter->GetTx().GetHash());
+ }
}
}
}
+ for (const uint256& parent_txid : parent_ids_to_add) {
+ // Relaying a transaction with a recent but unconfirmed parent.
+ if (WITH_LOCK(pfrom.m_tx_relay->cs_tx_inventory, return !pfrom.m_tx_relay->filterInventoryKnown.contains(parent_txid))) {
+ LOCK(cs_main);
+ State(pfrom.GetId())->m_recently_announced_invs.insert(parent_txid);
+ }
+ }
} else {
vNotFound.push_back(inv);
}
@@ -1799,7 +1807,7 @@ inline void static SendBlockTransactions(const CBlock& block, const BlockTransac
for (size_t i = 0; i < req.indexes.size(); i++) {
if (req.indexes[i] >= block.vtx.size()) {
LOCK(cs_main);
- Misbehaving(pfrom.GetId(), 100, strprintf("Peer %d sent us a getblocktxn with out-of-bounds tx indices", pfrom.GetId()));
+ Misbehaving(pfrom.GetId(), 100, "getblocktxn with out-of-bounds tx indices");
return;
}
resp.txn[i] = block.vtx[req.indexes[i]];
@@ -1848,7 +1856,7 @@ static void ProcessHeadersMessage(CNode& pfrom, CConnman& connman, ChainstateMan
UpdateBlockAvailability(pfrom.GetId(), headers.back().GetHash());
if (nodestate->nUnconnectingHeaders % MAX_UNCONNECTING_HEADERS == 0) {
- Misbehaving(pfrom.GetId(), 20);
+ Misbehaving(pfrom.GetId(), 20, strprintf("%d non-connecting headers", nodestate->nUnconnectingHeaders));
}
return;
}
@@ -1969,14 +1977,14 @@ static void ProcessHeadersMessage(CNode& pfrom, CConnman& connman, ChainstateMan
// until we have a headers chain that has at least
// nMinimumChainWork, even if a peer has a chain past our tip,
// as an anti-DoS measure.
- if (IsOutboundDisconnectionCandidate(pfrom)) {
+ if (pfrom.IsOutboundOrBlockRelayConn()) {
LogPrintf("Disconnecting outbound peer %d -- headers chain has insufficient work\n", pfrom.GetId());
pfrom.fDisconnect = true;
}
}
}
- if (!pfrom.fDisconnect && IsOutboundDisconnectionCandidate(pfrom) && nodestate->pindexBestKnownBlock != nullptr && pfrom.m_tx_relay != nullptr) {
+ if (!pfrom.fDisconnect && pfrom.IsOutboundOrBlockRelayConn() && nodestate->pindexBestKnownBlock != nullptr && pfrom.m_tx_relay != nullptr) {
// If this is an outbound full-relay peer, check to see if we should protect
// it from the bad/lagging chain logic.
// Note that block-relay-only peers are already implicitly protected, so we
@@ -2057,6 +2065,19 @@ void static ProcessOrphanTx(CConnman& connman, CTxMemPool& mempool, std::set<uin
// if we start doing this too early.
assert(recentRejects);
recentRejects->insert(orphanTx.GetWitnessHash());
+ // If the transaction failed for TX_INPUTS_NOT_STANDARD,
+ // then we know that the witness was irrelevant to the policy
+ // failure, since this check depends only on the txid
+ // (the scriptPubKey being spent is covered by the txid).
+ // Add the txid to the reject filter to prevent repeated
+ // processing of this transaction in the event that child
+ // transactions are later received (resulting in
+ // parent-fetching by txid via the orphan-handling logic).
+ if (orphan_state.GetResult() == TxValidationResult::TX_INPUTS_NOT_STANDARD && orphanTx.GetWitnessHash() != orphanTx.GetHash()) {
+ // We only add the txid if it differs from the wtxid, to
+ // avoid wasting entries in the rolling bloom filter.
+ recentRejects->insert(orphanTx.GetHash());
+ }
}
EraseOrphanTx(orphanHash);
done = true;
@@ -2070,7 +2091,7 @@ void static ProcessOrphanTx(CConnman& connman, CTxMemPool& mempool, std::set<uin
*
* May disconnect from the peer in the case of a bad request.
*
- * @param[in] pfrom The peer that we received the request from
+ * @param[in] peer The peer that we received the request from
* @param[in] chain_params Chain parameters
* @param[in] filter_type The filter type the request is for. Must be basic filters.
* @param[in] start_height The start height for the request
@@ -2080,7 +2101,7 @@ void static ProcessOrphanTx(CConnman& connman, CTxMemPool& mempool, std::set<uin
* @param[out] filter_index The filter index, if the request can be serviced.
* @return True if the request can be serviced.
*/
-static bool PrepareBlockFilterRequest(CNode& pfrom, const CChainParams& chain_params,
+static bool PrepareBlockFilterRequest(CNode& peer, const CChainParams& chain_params,
BlockFilterType filter_type, uint32_t start_height,
const uint256& stop_hash, uint32_t max_height_diff,
const CBlockIndex*& stop_index,
@@ -2088,11 +2109,11 @@ static bool PrepareBlockFilterRequest(CNode& pfrom, const CChainParams& chain_pa
{
const bool supported_filter_type =
(filter_type == BlockFilterType::BASIC &&
- gArgs.GetBoolArg("-peerblockfilters", DEFAULT_PEERBLOCKFILTERS));
+ (peer.GetLocalServices() & NODE_COMPACT_FILTERS));
if (!supported_filter_type) {
LogPrint(BCLog::NET, "peer %d requested unsupported block filter type: %d\n",
- pfrom.GetId(), static_cast<uint8_t>(filter_type));
- pfrom.fDisconnect = true;
+ peer.GetId(), static_cast<uint8_t>(filter_type));
+ peer.fDisconnect = true;
return false;
}
@@ -2103,8 +2124,8 @@ static bool PrepareBlockFilterRequest(CNode& pfrom, const CChainParams& chain_pa
// Check that the stop block exists and the peer would be allowed to fetch it.
if (!stop_index || !BlockRequestAllowed(stop_index, chain_params.GetConsensus())) {
LogPrint(BCLog::NET, "peer %d requested invalid block hash: %s\n",
- pfrom.GetId(), stop_hash.ToString());
- pfrom.fDisconnect = true;
+ peer.GetId(), stop_hash.ToString());
+ peer.fDisconnect = true;
return false;
}
}
@@ -2113,14 +2134,14 @@ static bool PrepareBlockFilterRequest(CNode& pfrom, const CChainParams& chain_pa
if (start_height > stop_height) {
LogPrint(BCLog::NET, "peer %d sent invalid getcfilters/getcfheaders with " /* Continued */
"start height %d and stop height %d\n",
- pfrom.GetId(), start_height, stop_height);
- pfrom.fDisconnect = true;
+ peer.GetId(), start_height, stop_height);
+ peer.fDisconnect = true;
return false;
}
if (stop_height - start_height >= max_height_diff) {
LogPrint(BCLog::NET, "peer %d requested too many cfilters/cfheaders: %d / %d\n",
- pfrom.GetId(), stop_height - start_height + 1, max_height_diff);
- pfrom.fDisconnect = true;
+ peer.GetId(), stop_height - start_height + 1, max_height_diff);
+ peer.fDisconnect = true;
return false;
}
@@ -2138,12 +2159,12 @@ static bool PrepareBlockFilterRequest(CNode& pfrom, const CChainParams& chain_pa
*
* May disconnect from the peer in the case of a bad request.
*
- * @param[in] pfrom The peer that we received the request from
+ * @param[in] peer The peer that we received the request from
* @param[in] vRecv The raw message received
* @param[in] chain_params Chain parameters
* @param[in] connman Pointer to the connection manager
*/
-static void ProcessGetCFilters(CNode& pfrom, CDataStream& vRecv, const CChainParams& chain_params,
+static void ProcessGetCFilters(CNode& peer, CDataStream& vRecv, const CChainParams& chain_params,
CConnman& connman)
{
uint8_t filter_type_ser;
@@ -2156,13 +2177,12 @@ static void ProcessGetCFilters(CNode& pfrom, CDataStream& vRecv, const CChainPar
const CBlockIndex* stop_index;
BlockFilterIndex* filter_index;
- if (!PrepareBlockFilterRequest(pfrom, chain_params, filter_type, start_height, stop_hash,
+ if (!PrepareBlockFilterRequest(peer, chain_params, filter_type, start_height, stop_hash,
MAX_GETCFILTERS_SIZE, stop_index, filter_index)) {
return;
}
std::vector<BlockFilter> filters;
-
if (!filter_index->LookupFilterRange(start_height, stop_index, filters)) {
LogPrint(BCLog::NET, "Failed to find block filter in index: filter_type=%s, start_height=%d, stop_hash=%s\n",
BlockFilterTypeName(filter_type), start_height, stop_hash.ToString());
@@ -2170,9 +2190,9 @@ static void ProcessGetCFilters(CNode& pfrom, CDataStream& vRecv, const CChainPar
}
for (const auto& filter : filters) {
- CSerializedNetMsg msg = CNetMsgMaker(pfrom.GetSendVersion())
+ CSerializedNetMsg msg = CNetMsgMaker(peer.GetSendVersion())
.Make(NetMsgType::CFILTER, filter);
- connman.PushMessage(&pfrom, std::move(msg));
+ connman.PushMessage(&peer, std::move(msg));
}
}
@@ -2181,12 +2201,12 @@ static void ProcessGetCFilters(CNode& pfrom, CDataStream& vRecv, const CChainPar
*
* May disconnect from the peer in the case of a bad request.
*
- * @param[in] pfrom The peer that we received the request from
+ * @param[in] peer The peer that we received the request from
* @param[in] vRecv The raw message received
* @param[in] chain_params Chain parameters
* @param[in] connman Pointer to the connection manager
*/
-static void ProcessGetCFHeaders(CNode& pfrom, CDataStream& vRecv, const CChainParams& chain_params,
+static void ProcessGetCFHeaders(CNode& peer, CDataStream& vRecv, const CChainParams& chain_params,
CConnman& connman)
{
uint8_t filter_type_ser;
@@ -2199,7 +2219,7 @@ static void ProcessGetCFHeaders(CNode& pfrom, CDataStream& vRecv, const CChainPa
const CBlockIndex* stop_index;
BlockFilterIndex* filter_index;
- if (!PrepareBlockFilterRequest(pfrom, chain_params, filter_type, start_height, stop_hash,
+ if (!PrepareBlockFilterRequest(peer, chain_params, filter_type, start_height, stop_hash,
MAX_GETCFHEADERS_SIZE, stop_index, filter_index)) {
return;
}
@@ -2222,13 +2242,13 @@ static void ProcessGetCFHeaders(CNode& pfrom, CDataStream& vRecv, const CChainPa
return;
}
- CSerializedNetMsg msg = CNetMsgMaker(pfrom.GetSendVersion())
+ CSerializedNetMsg msg = CNetMsgMaker(peer.GetSendVersion())
.Make(NetMsgType::CFHEADERS,
filter_type_ser,
stop_index->GetBlockHash(),
prev_header,
filter_hashes);
- connman.PushMessage(&pfrom, std::move(msg));
+ connman.PushMessage(&peer, std::move(msg));
}
/**
@@ -2236,12 +2256,12 @@ static void ProcessGetCFHeaders(CNode& pfrom, CDataStream& vRecv, const CChainPa
*
* May disconnect from the peer in the case of a bad request.
*
- * @param[in] pfrom The peer that we received the request from
+ * @param[in] peer The peer that we received the request from
* @param[in] vRecv The raw message received
* @param[in] chain_params Chain parameters
* @param[in] connman Pointer to the connection manager
*/
-static void ProcessGetCFCheckPt(CNode& pfrom, CDataStream& vRecv, const CChainParams& chain_params,
+static void ProcessGetCFCheckPt(CNode& peer, CDataStream& vRecv, const CChainParams& chain_params,
CConnman& connman)
{
uint8_t filter_type_ser;
@@ -2253,7 +2273,7 @@ static void ProcessGetCFCheckPt(CNode& pfrom, CDataStream& vRecv, const CChainPa
const CBlockIndex* stop_index;
BlockFilterIndex* filter_index;
- if (!PrepareBlockFilterRequest(pfrom, chain_params, filter_type, /*start_height=*/0, stop_hash,
+ if (!PrepareBlockFilterRequest(peer, chain_params, filter_type, /*start_height=*/0, stop_hash,
/*max_height_diff=*/std::numeric_limits<uint32_t>::max(),
stop_index, filter_index)) {
return;
@@ -2274,25 +2294,17 @@ static void ProcessGetCFCheckPt(CNode& pfrom, CDataStream& vRecv, const CChainPa
}
}
- CSerializedNetMsg msg = CNetMsgMaker(pfrom.GetSendVersion())
+ CSerializedNetMsg msg = CNetMsgMaker(peer.GetSendVersion())
.Make(NetMsgType::CFCHECKPT,
filter_type_ser,
stop_index->GetBlockHash(),
headers);
- connman.PushMessage(&pfrom, std::move(msg));
+ connman.PushMessage(&peer, std::move(msg));
}
-void ProcessMessage(
- CNode& pfrom,
- const std::string& msg_type,
- CDataStream& vRecv,
- const std::chrono::microseconds time_received,
- const CChainParams& chainparams,
- ChainstateManager& chainman,
- CTxMemPool& mempool,
- CConnman& connman,
- BanMan* banman,
- const std::atomic<bool>& interruptMsgProc)
+void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv,
+ const std::chrono::microseconds time_received,
+ const CChainParams& chainparams, const std::atomic<bool>& interruptMsgProc)
{
LogPrint(BCLog::NET, "received: %s (%u bytes) peer=%d\n", SanitizeString(msg_type), vRecv.size(), pfrom.GetId());
if (gArgs.IsArgSet("-dropmessagestest") && GetRand(gArgs.GetArg("-dropmessagestest", 0)) == 0)
@@ -2307,7 +2319,7 @@ void ProcessMessage(
if (pfrom.nVersion != 0)
{
LOCK(cs_main);
- Misbehaving(pfrom.GetId(), 1);
+ Misbehaving(pfrom.GetId(), 1, "redundant version message");
return;
}
@@ -2326,11 +2338,11 @@ void ProcessMessage(
vRecv >> nVersion >> nServiceInt >> nTime >> addrMe;
nSendVersion = std::min(nVersion, PROTOCOL_VERSION);
nServices = ServiceFlags(nServiceInt);
- if (!pfrom.fInbound)
+ if (!pfrom.IsInboundConn())
{
- connman.SetServices(pfrom.addr, nServices);
+ m_connman.SetServices(pfrom.addr, nServices);
}
- if (!pfrom.fInbound && !pfrom.fFeeler && !pfrom.m_manual_connection && !HasAllDesirableServiceFlags(nServices))
+ if (pfrom.ExpectServicesFromConn() && !HasAllDesirableServiceFlags(nServices))
{
LogPrint(BCLog::NET, "peer=%d does not offer the expected services (%08x offered, %08x expected); disconnecting\n", pfrom.GetId(), nServices, GetDesirableServiceFlags(nServices));
pfrom.fDisconnect = true;
@@ -2357,27 +2369,27 @@ void ProcessMessage(
if (!vRecv.empty())
vRecv >> fRelay;
// Disconnect if we connected to ourself
- if (pfrom.fInbound && !connman.CheckIncomingNonce(nNonce))
+ if (pfrom.IsInboundConn() && !m_connman.CheckIncomingNonce(nNonce))
{
LogPrintf("connected to self at %s, disconnecting\n", pfrom.addr.ToString());
pfrom.fDisconnect = true;
return;
}
- if (pfrom.fInbound && addrMe.IsRoutable())
+ if (pfrom.IsInboundConn() && addrMe.IsRoutable())
{
SeenLocal(addrMe);
}
// Be shy and don't send version until we hear
- if (pfrom.fInbound)
- PushNodeVersion(pfrom, connman, GetAdjustedTime());
+ if (pfrom.IsInboundConn())
+ PushNodeVersion(pfrom, m_connman, GetAdjustedTime());
if (nVersion >= WTXID_RELAY_VERSION) {
- connman.PushMessage(&pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::WTXIDRELAY));
+ m_connman.PushMessage(&pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::WTXIDRELAY));
}
- connman.PushMessage(&pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERACK));
+ m_connman.PushMessage(&pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERACK));
pfrom.nServices = nServices;
pfrom.SetAddrLocal(addrMe);
@@ -2414,7 +2426,7 @@ void ProcessMessage(
UpdatePreferredDownload(pfrom, State(pfrom.GetId()));
}
- if (!pfrom.fInbound && pfrom.IsAddrRelayPeer())
+ if (!pfrom.IsInboundConn() && pfrom.IsAddrRelayPeer())
{
// Advertise our address
if (fListen && !::ChainstateActive().IsInitialBlockDownload())
@@ -2433,9 +2445,9 @@ void ProcessMessage(
}
// Get recent addresses
- connman.PushMessage(&pfrom, CNetMsgMaker(nSendVersion).Make(NetMsgType::GETADDR));
+ m_connman.PushMessage(&pfrom, CNetMsgMaker(nSendVersion).Make(NetMsgType::GETADDR));
pfrom.fGetAddr = true;
- connman.MarkAddressGood(pfrom.addr);
+ m_connman.MarkAddressGood(pfrom.addr);
}
std::string remoteAddr;
@@ -2454,12 +2466,11 @@ void ProcessMessage(
// If the peer is old enough to have the old alert system, send it the final alert.
if (pfrom.nVersion <= 70012) {
CDataStream finalAlert(ParseHex("60010000000000000000000000ffffff7f00000000ffffff7ffeffff7f01ffffff7f00000000ffffff7f00ffffff7f002f555247454e543a20416c657274206b657920636f6d70726f6d697365642c2075706772616465207265717569726564004630440220653febd6410f470f6bae11cad19c48413becb1ac2c17f908fd0fd53bdc3abd5202206d0e9c96fe88d4a0f01ed9dedae2b6f9e00da94cad0fecaae66ecf689bf71b50"), SER_NETWORK, PROTOCOL_VERSION);
- connman.PushMessage(&pfrom, CNetMsgMaker(nSendVersion).Make("alert", finalAlert));
+ m_connman.PushMessage(&pfrom, CNetMsgMaker(nSendVersion).Make("alert", finalAlert));
}
// Feeler connections exist only to verify if address is online.
- if (pfrom.fFeeler) {
- assert(pfrom.fInbound == false);
+ if (pfrom.IsFeelerConn()) {
pfrom.fDisconnect = true;
}
return;
@@ -2468,7 +2479,7 @@ void ProcessMessage(
if (pfrom.nVersion == 0) {
// Must have a version message before anything else
LOCK(cs_main);
- Misbehaving(pfrom.GetId(), 1);
+ Misbehaving(pfrom.GetId(), 1, "non-version message before version handshake");
return;
}
@@ -2479,7 +2490,7 @@ void ProcessMessage(
{
pfrom.SetRecvVersion(std::min(pfrom.nVersion.load(), PROTOCOL_VERSION));
- if (!pfrom.fInbound) {
+ if (!pfrom.IsInboundConn()) {
// Mark this node as currently connected, so we update its timestamp later.
LOCK(cs_main);
State(pfrom.GetId())->fCurrentlyConnected = true;
@@ -2494,7 +2505,7 @@ void ProcessMessage(
// We send this to non-NODE NETWORK peers as well, because even
// non-NODE NETWORK peers can announce blocks (such as pruning
// nodes)
- connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDHEADERS));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDHEADERS));
}
if (pfrom.nVersion >= SHORT_IDS_BLOCKS_VERSION) {
// Tell our peer we are willing to provide version 1 or 2 cmpctblocks
@@ -2505,9 +2516,9 @@ void ProcessMessage(
bool fAnnounceUsingCMPCTBLOCK = false;
uint64_t nCMPCTBLOCKVersion = 2;
if (pfrom.GetLocalServices() & NODE_WITNESS)
- connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
nCMPCTBLOCKVersion = 1;
- connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
}
pfrom.fSuccessfullyConnected = true;
return;
@@ -2535,7 +2546,7 @@ void ProcessMessage(
if (!pfrom.fSuccessfullyConnected) {
// Must have a verack message before anything else
LOCK(cs_main);
- Misbehaving(pfrom.GetId(), 1);
+ Misbehaving(pfrom.GetId(), 1, "non-verack message before version handshake");
return;
}
@@ -2546,7 +2557,7 @@ void ProcessMessage(
if (!pfrom.IsAddrRelayPeer()) {
return;
}
- if (vAddr.size() > 1000)
+ if (vAddr.size() > MAX_ADDR_TO_SEND)
{
LOCK(cs_main);
Misbehaving(pfrom.GetId(), 20, strprintf("addr message size = %u", vAddr.size()));
@@ -2571,7 +2582,7 @@ void ProcessMessage(
if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60)
addr.nTime = nNow - 5 * 24 * 60 * 60;
pfrom.AddAddressKnown(addr);
- if (banman && (banman->IsDiscouraged(addr) || banman->IsBanned(addr))) {
+ if (m_banman && (m_banman->IsDiscouraged(addr) || m_banman->IsBanned(addr))) {
// Do not process banned/discouraged addresses beyond remembering we received them
continue;
}
@@ -2579,16 +2590,16 @@ void ProcessMessage(
if (addr.nTime > nSince && !pfrom.fGetAddr && vAddr.size() <= 10 && addr.IsRoutable())
{
// Relay to a limited number of other nodes
- RelayAddress(addr, fReachable, connman);
+ RelayAddress(addr, fReachable, m_connman);
}
// Do not store addresses outside our network
if (fReachable)
vAddrOk.push_back(addr);
}
- connman.AddNewAddresses(vAddrOk, pfrom.addr, 2 * 60 * 60);
+ m_connman.AddNewAddresses(vAddrOk, pfrom.addr, 2 * 60 * 60);
if (vAddr.size() < 1000)
pfrom.fGetAddr = false;
- if (pfrom.fOneShot)
+ if (pfrom.IsAddrFetchConn())
pfrom.fDisconnect = true;
return;
}
@@ -2652,17 +2663,19 @@ void ProcessMessage(
if (interruptMsgProc)
return;
- // ignore INVs that don't match wtxidrelay setting
+ // Ignore INVs that don't match wtxidrelay setting.
+ // Note that orphan parent fetching always uses MSG_TX GETDATAs regardless of the wtxidrelay setting.
+ // This is fine as no INV messages are involved in that process.
if (State(pfrom.GetId())->m_wtxid_relay) {
- if (inv.type == MSG_TX) continue;
+ if (inv.IsMsgTx()) continue;
} else {
- if (inv.type == MSG_WTX) continue;
+ if (inv.IsMsgWtx()) continue;
}
- bool fAlreadyHave = AlreadyHave(inv, mempool);
+ bool fAlreadyHave = AlreadyHave(inv, m_mempool);
LogPrint(BCLog::NET, "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom.GetId());
- if (inv.type == MSG_TX) {
+ if (inv.IsMsgTx()) {
inv.type |= nFetchFlags;
}
@@ -2682,14 +2695,14 @@ void ProcessMessage(
LogPrint(BCLog::NET, "transaction (%s) inv sent in violation of protocol, disconnecting peer=%d\n", inv.hash.ToString(), pfrom.GetId());
pfrom.fDisconnect = true;
return;
- } else if (!fAlreadyHave && !chainman.ActiveChainstate().IsInitialBlockDownload()) {
- RequestTx(State(pfrom.GetId()), inv.hash, current_time);
+ } else if (!fAlreadyHave && !m_chainman.ActiveChainstate().IsInitialBlockDownload()) {
+ RequestTx(State(pfrom.GetId()), ToGenTxid(inv), current_time);
}
}
}
if (best_block != nullptr) {
- connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), *best_block));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), *best_block));
LogPrint(BCLog::NET, "getheaders (%d) %s to peer=%d\n", pindexBestHeader->nHeight, best_block->ToString(), pfrom.GetId());
}
@@ -2713,7 +2726,7 @@ void ProcessMessage(
}
pfrom.vRecvGetData.insert(pfrom.vRecvGetData.end(), vInv.begin(), vInv.end());
- ProcessGetData(pfrom, chainparams, connman, mempool, interruptMsgProc);
+ ProcessGetData(pfrom, chainparams, m_connman, m_mempool, interruptMsgProc);
return;
}
@@ -2797,7 +2810,7 @@ void ProcessMessage(
// Unlock cs_most_recent_block to avoid cs_main lock inversion
}
if (recent_block) {
- SendBlockTransactions(*recent_block, req, pfrom, connman);
+ SendBlockTransactions(*recent_block, req, pfrom, m_connman);
return;
}
@@ -2830,7 +2843,7 @@ void ProcessMessage(
bool ret = ReadBlockFromDisk(block, pindex, chainparams.GetConsensus());
assert(ret);
- SendBlockTransactions(block, req, pfrom, connman);
+ SendBlockTransactions(block, req, pfrom, m_connman);
return;
}
@@ -2897,7 +2910,7 @@ void ProcessMessage(
// will re-announce the new block via headers (or compact blocks again)
// in the SendMessages logic.
nodestate->pindexBestHeaderSent = pindex ? pindex : ::ChainActive().Tip();
- connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::HEADERS, vHeaders));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::HEADERS, vHeaders));
return;
}
@@ -2936,15 +2949,17 @@ void ProcessMessage(
TxValidationState state;
- nodestate->m_tx_download.m_tx_announced.erase(hash);
- nodestate->m_tx_download.m_tx_in_flight.erase(hash);
- EraseTxRequest(hash);
+ for (const GenTxid& gtxid : {GenTxid(false, txid), GenTxid(true, wtxid)}) {
+ nodestate->m_tx_download.m_tx_announced.erase(gtxid.GetHash());
+ nodestate->m_tx_download.m_tx_in_flight.erase(gtxid.GetHash());
+ EraseTxRequest(gtxid);
+ }
std::list<CTransactionRef> lRemovedTxn;
// We do the AlreadyHave() check using wtxid, rather than txid - in the
// absence of witness malleation, this is strictly better, because the
- // recent rejects filter may contain the wtxid but will never contain
+ // recent rejects filter may contain the wtxid but rarely contains
// the txid of a segwit transaction that has been rejected.
// In the presence of witness malleation, it's possible that by only
// doing the check with wtxid, we could overlook a transaction which
@@ -2954,10 +2969,10 @@ void ProcessMessage(
// already; and an adversary can already relay us old transactions
// (older than our recency filter) if trying to DoS us, without any need
// for witness malleation.
- if (!AlreadyHave(CInv(MSG_WTX, wtxid), mempool) &&
- AcceptToMemoryPool(mempool, state, ptx, &lRemovedTxn, false /* bypass_limits */, 0 /* nAbsurdFee */)) {
- mempool.check(&::ChainstateActive().CoinsTip());
- RelayTransaction(tx.GetHash(), tx.GetWitnessHash(), connman);
+ if (!AlreadyHave(CInv(MSG_WTX, wtxid), m_mempool) &&
+ AcceptToMemoryPool(m_mempool, state, ptx, &lRemovedTxn, false /* bypass_limits */, 0 /* nAbsurdFee */)) {
+ m_mempool.check(&::ChainstateActive().CoinsTip());
+ RelayTransaction(tx.GetHash(), tx.GetWitnessHash(), m_connman);
for (unsigned int i = 0; i < tx.vout.size(); i++) {
auto it_by_prev = mapOrphanTransactionsByPrev.find(COutPoint(txid, i));
if (it_by_prev != mapOrphanTransactionsByPrev.end()) {
@@ -2972,16 +2987,27 @@ void ProcessMessage(
LogPrint(BCLog::MEMPOOL, "AcceptToMemoryPool: peer=%d: accepted %s (poolsz %u txn, %u kB)\n",
pfrom.GetId(),
tx.GetHash().ToString(),
- mempool.size(), mempool.DynamicMemoryUsage() / 1000);
+ m_mempool.size(), m_mempool.DynamicMemoryUsage() / 1000);
// Recursively process any orphan transactions that depended on this one
- ProcessOrphanTx(connman, mempool, pfrom.orphan_work_set, lRemovedTxn);
+ ProcessOrphanTx(m_connman, m_mempool, pfrom.orphan_work_set, lRemovedTxn);
}
else if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS)
{
bool fRejectedParents = false; // It may be the case that the orphans parents have all been rejected
+
+ // Deduplicate parent txids, so that we don't have to loop over
+ // the same parent txid more than once down below.
+ std::vector<uint256> unique_parents;
+ unique_parents.reserve(tx.vin.size());
for (const CTxIn& txin : tx.vin) {
- if (recentRejects->contains(txin.prevout.hash)) {
+ // We start with all parents, and then remove duplicates below.
+ unique_parents.push_back(txin.prevout.hash);
+ }
+ std::sort(unique_parents.begin(), unique_parents.end());
+ unique_parents.erase(std::unique(unique_parents.begin(), unique_parents.end()), unique_parents.end());
+ for (const uint256& parent_txid : unique_parents) {
+ if (recentRejects->contains(parent_txid)) {
fRejectedParents = true;
break;
}
@@ -2990,17 +3016,15 @@ void ProcessMessage(
uint32_t nFetchFlags = GetFetchFlags(pfrom);
const auto current_time = GetTime<std::chrono::microseconds>();
- if (!State(pfrom.GetId())->m_wtxid_relay) {
- for (const CTxIn& txin : tx.vin) {
- // Here, we only have the txid (and not wtxid) of the
- // inputs, so we only request parents from
- // non-wtxid-relay peers.
- // Eventually we should replace this with an improved
- // protocol for getting all unconfirmed parents.
- CInv _inv(MSG_TX | nFetchFlags, txin.prevout.hash);
- pfrom.AddKnownTx(txin.prevout.hash);
- if (!AlreadyHave(_inv, mempool)) RequestTx(State(pfrom.GetId()), _inv.hash, current_time);
- }
+ for (const uint256& parent_txid : unique_parents) {
+ // Here, we only have the txid (and not wtxid) of the
+ // inputs, so we only request in txid mode, even for
+ // wtxidrelay peers.
+ // Eventually we should replace this with an improved
+ // protocol for getting all unconfirmed parents.
+ CInv _inv(MSG_TX | nFetchFlags, parent_txid);
+ pfrom.AddKnownTx(parent_txid);
+ if (!AlreadyHave(_inv, m_mempool)) RequestTx(State(pfrom.GetId()), ToGenTxid(_inv), current_time);
}
AddOrphanTx(ptx, pfrom.GetId());
@@ -3038,6 +3062,17 @@ void ProcessMessage(
// if we start doing this too early.
assert(recentRejects);
recentRejects->insert(tx.GetWitnessHash());
+ // If the transaction failed for TX_INPUTS_NOT_STANDARD,
+ // then we know that the witness was irrelevant to the policy
+ // failure, since this check depends only on the txid
+ // (the scriptPubKey being spent is covered by the txid).
+ // Add the txid to the reject filter to prevent repeated
+ // processing of this transaction in the event that child
+ // transactions are later received (resulting in
+ // parent-fetching by txid via the orphan-handling logic).
+ if (state.GetResult() == TxValidationResult::TX_INPUTS_NOT_STANDARD && tx.GetWitnessHash() != tx.GetHash()) {
+ recentRejects->insert(tx.GetHash());
+ }
if (RecursiveDynamicUsage(*ptx) < 100000) {
AddToCompactExtraTransactions(ptx);
}
@@ -3050,11 +3085,11 @@ void ProcessMessage(
// if they were already in the mempool,
// allowing the node to function as a gateway for
// nodes hidden behind it.
- if (!mempool.exists(tx.GetHash())) {
+ if (!m_mempool.exists(tx.GetHash())) {
LogPrintf("Not relaying non-mempool transaction %s from forcerelay peer=%d\n", tx.GetHash().ToString(), pfrom.GetId());
} else {
LogPrintf("Force relaying tx %s from peer=%d\n", tx.GetHash().ToString(), pfrom.GetId());
- RelayTransaction(tx.GetHash(), tx.GetWitnessHash(), connman);
+ RelayTransaction(tx.GetHash(), tx.GetWitnessHash(), m_connman);
}
}
}
@@ -3107,7 +3142,7 @@ void ProcessMessage(
if (!LookupBlockIndex(cmpctblock.header.hashPrevBlock)) {
// Doesn't connect (or is genesis), instead of DoSing in AcceptBlockHeader, request deeper headers
if (!::ChainstateActive().IsInitialBlockDownload())
- connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), uint256()));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), uint256()));
return;
}
@@ -3118,7 +3153,7 @@ void ProcessMessage(
const CBlockIndex *pindex = nullptr;
BlockValidationState state;
- if (!chainman.ProcessNewBlockHeaders({cmpctblock.header}, state, chainparams, &pindex)) {
+ if (!m_chainman.ProcessNewBlockHeaders({cmpctblock.header}, state, chainparams, &pindex)) {
if (state.IsInvalid()) {
MaybePunishNodeForBlock(pfrom.GetId(), state, /*via_compact_block*/ true, "invalid header via cmpctblock");
return;
@@ -3168,7 +3203,7 @@ void ProcessMessage(
// so we just grab the block via normal getdata
std::vector<CInv> vInv(1);
vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom), cmpctblock.header.GetHash());
- connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv));
}
return;
}
@@ -3189,9 +3224,9 @@ void ProcessMessage(
if ((!fAlreadyInFlight && nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) ||
(fAlreadyInFlight && blockInFlightIt->second.first == pfrom.GetId())) {
std::list<QueuedBlock>::iterator* queuedBlockIt = nullptr;
- if (!MarkBlockAsInFlight(mempool, pfrom.GetId(), pindex->GetBlockHash(), pindex, &queuedBlockIt)) {
+ if (!MarkBlockAsInFlight(m_mempool, pfrom.GetId(), pindex->GetBlockHash(), pindex, &queuedBlockIt)) {
if (!(*queuedBlockIt)->partialBlock)
- (*queuedBlockIt)->partialBlock.reset(new PartiallyDownloadedBlock(&mempool));
+ (*queuedBlockIt)->partialBlock.reset(new PartiallyDownloadedBlock(&m_mempool));
else {
// The block was already in flight using compact blocks from the same peer
LogPrint(BCLog::NET, "Peer sent us compact block we were already syncing!\n");
@@ -3203,13 +3238,13 @@ void ProcessMessage(
ReadStatus status = partialBlock.InitData(cmpctblock, vExtraTxnForCompact);
if (status == READ_STATUS_INVALID) {
MarkBlockAsReceived(pindex->GetBlockHash()); // Reset in-flight state in case Misbehaving does not result in a disconnect
- Misbehaving(pfrom.GetId(), 100, strprintf("Peer %d sent us invalid compact block\n", pfrom.GetId()));
+ Misbehaving(pfrom.GetId(), 100, "invalid compact block");
return;
} else if (status == READ_STATUS_FAILED) {
// Duplicate txindexes, the block is now in-flight, so just request it
std::vector<CInv> vInv(1);
vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom), cmpctblock.header.GetHash());
- connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv));
return;
}
@@ -3226,7 +3261,7 @@ void ProcessMessage(
fProcessBLOCKTXN = true;
} else {
req.blockhash = pindex->GetBlockHash();
- connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETBLOCKTXN, req));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETBLOCKTXN, req));
}
} else {
// This block is either already in flight from a different
@@ -3234,7 +3269,7 @@ void ProcessMessage(
// download from.
// Optimistically try to reconstruct anyway since we might be
// able to without any round trips.
- PartiallyDownloadedBlock tempBlock(&mempool);
+ PartiallyDownloadedBlock tempBlock(&m_mempool);
ReadStatus status = tempBlock.InitData(cmpctblock, vExtraTxnForCompact);
if (status != READ_STATUS_OK) {
// TODO: don't ignore failures
@@ -3252,7 +3287,7 @@ void ProcessMessage(
// mempool will probably be useless - request the block normally
std::vector<CInv> vInv(1);
vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom), cmpctblock.header.GetHash());
- connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv));
return;
} else {
// If this was an announce-cmpctblock, we want the same treatment as a header message
@@ -3262,7 +3297,7 @@ void ProcessMessage(
} // cs_main
if (fProcessBLOCKTXN)
- return ProcessMessage(pfrom, NetMsgType::BLOCKTXN, blockTxnMsg, time_received, chainparams, chainman, mempool, connman, banman, interruptMsgProc);
+ return ProcessMessage(pfrom, NetMsgType::BLOCKTXN, blockTxnMsg, time_received, chainparams, interruptMsgProc);
if (fRevertToHeaderProcessing) {
// Headers received from HB compact block peers are permitted to be
@@ -3270,7 +3305,7 @@ void ProcessMessage(
// the peer if the header turns out to be for an invalid block.
// Note that if a peer tries to build on an invalid chain, that
// will be detected and the peer will be disconnected/discouraged.
- return ProcessHeadersMessage(pfrom, connman, chainman, mempool, {cmpctblock.header}, chainparams, /*via_compact_block=*/true);
+ return ProcessHeadersMessage(pfrom, m_connman, m_chainman, m_mempool, {cmpctblock.header}, chainparams, /*via_compact_block=*/true);
}
if (fBlockReconstructed) {
@@ -3290,7 +3325,7 @@ void ProcessMessage(
// we have a chain with at least nMinimumChainWork), and we ignore
// compact blocks with less work than our tip, it is safe to treat
// reconstructed compact blocks as having been requested.
- chainman.ProcessNewBlock(chainparams, pblock, /*fForceProcessing=*/true, &fNewBlock);
+ m_chainman.ProcessNewBlock(chainparams, pblock, /*fForceProcessing=*/true, &fNewBlock);
if (fNewBlock) {
pfrom.nLastBlockTime = GetTime();
} else {
@@ -3336,13 +3371,13 @@ void ProcessMessage(
ReadStatus status = partialBlock.FillBlock(*pblock, resp.txn);
if (status == READ_STATUS_INVALID) {
MarkBlockAsReceived(resp.blockhash); // Reset in-flight state in case Misbehaving does not result in a disconnect
- Misbehaving(pfrom.GetId(), 100, strprintf("Peer %d sent us invalid compact block/non-matching block transactions\n", pfrom.GetId()));
+ Misbehaving(pfrom.GetId(), 100, "invalid compact block/non-matching block transactions");
return;
} else if (status == READ_STATUS_FAILED) {
// Might have collided, fall back to getdata now :(
std::vector<CInv> invs;
invs.push_back(CInv(MSG_BLOCK | GetFetchFlags(pfrom), resp.blockhash));
- connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, invs));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, invs));
} else {
// Block is either okay, or possibly we received
// READ_STATUS_CHECKBLOCK_FAILED.
@@ -3380,7 +3415,7 @@ void ProcessMessage(
// disk-space attacks), but this should be safe due to the
// protections in the compact block handler -- see related comment
// in compact block optimistic reconstruction handling.
- chainman.ProcessNewBlock(chainparams, pblock, /*fForceProcessing=*/true, &fNewBlock);
+ m_chainman.ProcessNewBlock(chainparams, pblock, /*fForceProcessing=*/true, &fNewBlock);
if (fNewBlock) {
pfrom.nLastBlockTime = GetTime();
} else {
@@ -3414,7 +3449,7 @@ void ProcessMessage(
ReadCompactSize(vRecv); // ignore tx count; assume it is 0.
}
- return ProcessHeadersMessage(pfrom, connman, chainman, mempool, headers, chainparams, /*via_compact_block=*/false);
+ return ProcessHeadersMessage(pfrom, m_connman, m_chainman, m_mempool, headers, chainparams, /*via_compact_block=*/false);
}
if (msg_type == NetMsgType::BLOCK)
@@ -3443,7 +3478,7 @@ void ProcessMessage(
mapBlockSource.emplace(hash, std::make_pair(pfrom.GetId(), true));
}
bool fNewBlock = false;
- chainman.ProcessNewBlock(chainparams, pblock, forceProcessing, &fNewBlock);
+ m_chainman.ProcessNewBlock(chainparams, pblock, forceProcessing, &fNewBlock);
if (fNewBlock) {
pfrom.nLastBlockTime = GetTime();
} else {
@@ -3459,7 +3494,7 @@ void ProcessMessage(
// to users' AddrMan and later request them by sending getaddr messages.
// Making nodes which are behind NAT and can only make outgoing connections ignore
// the getaddr message mitigates the attack.
- if (!pfrom.fInbound) {
+ if (!pfrom.IsInboundConn()) {
LogPrint(BCLog::NET, "Ignoring \"getaddr\" from outbound connection. peer=%d\n", pfrom.GetId());
return;
}
@@ -3477,13 +3512,15 @@ void ProcessMessage(
pfrom.fSentAddr = true;
pfrom.vAddrToSend.clear();
- std::vector<CAddress> vAddr = connman.GetAddresses();
+ std::vector<CAddress> vAddr;
+ if (pfrom.HasPermission(PF_ADDR)) {
+ vAddr = m_connman.GetAddresses(MAX_ADDR_TO_SEND, MAX_PCT_ADDR_TO_SEND);
+ } else {
+ vAddr = m_connman.GetAddresses(pfrom.addr.GetNetwork(), MAX_ADDR_TO_SEND, MAX_PCT_ADDR_TO_SEND);
+ }
FastRandomContext insecure_rand;
for (const CAddress &addr : vAddr) {
- bool banned_or_discouraged = banman && (banman->IsDiscouraged(addr) || banman->IsBanned(addr));
- if (!banned_or_discouraged) {
- pfrom.PushAddress(addr, insecure_rand);
- }
+ pfrom.PushAddress(addr, insecure_rand);
}
return;
}
@@ -3499,7 +3536,7 @@ void ProcessMessage(
return;
}
- if (connman.OutboundTargetReached(false) && !pfrom.HasPermission(PF_MEMPOOL))
+ if (m_connman.OutboundTargetReached(false) && !pfrom.HasPermission(PF_MEMPOOL))
{
if (!pfrom.HasPermission(PF_NOBAN))
{
@@ -3532,7 +3569,7 @@ void ProcessMessage(
// it, if the remote node sends a ping once per second and this node takes 5
// seconds to respond to each, the 5th ping the remote sends would appear to
// return very quickly.
- connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::PONG, nonce));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::PONG, nonce));
}
return;
}
@@ -3605,7 +3642,7 @@ void ProcessMessage(
{
// There is no excuse for sending a too-large filter
LOCK(cs_main);
- Misbehaving(pfrom.GetId(), 100);
+ Misbehaving(pfrom.GetId(), 100, "too-large bloom filter");
}
else if (pfrom.m_tx_relay != nullptr)
{
@@ -3639,7 +3676,7 @@ void ProcessMessage(
}
if (bad) {
LOCK(cs_main);
- Misbehaving(pfrom.GetId(), 100);
+ Misbehaving(pfrom.GetId(), 100, "bad filteradd message");
}
return;
}
@@ -3672,17 +3709,17 @@ void ProcessMessage(
}
if (msg_type == NetMsgType::GETCFILTERS) {
- ProcessGetCFilters(pfrom, vRecv, chainparams, connman);
+ ProcessGetCFilters(pfrom, vRecv, chainparams, m_connman);
return;
}
if (msg_type == NetMsgType::GETCFHEADERS) {
- ProcessGetCFHeaders(pfrom, vRecv, chainparams, connman);
+ ProcessGetCFHeaders(pfrom, vRecv, chainparams, m_connman);
return;
}
if (msg_type == NetMsgType::GETCFCHECKPT) {
- ProcessGetCFCheckPt(pfrom, vRecv, chainparams, connman);
+ ProcessGetCFCheckPt(pfrom, vRecv, chainparams, m_connman);
return;
}
@@ -3694,7 +3731,7 @@ void ProcessMessage(
vRecv >> vInv;
if (vInv.size() <= MAX_PEER_TX_IN_FLIGHT + MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
for (CInv &inv : vInv) {
- if (inv.type == MSG_TX || inv.type == MSG_WITNESS_TX || inv.type == MSG_WTX) {
+ if (inv.IsGenTxMsg()) {
// If we receive a NOTFOUND message for a txid we requested, erase
// it from our data structures for this peer.
auto in_flight_it = state->m_tx_download.m_tx_in_flight.find(inv.hash);
@@ -3716,32 +3753,49 @@ void ProcessMessage(
return;
}
+/** Maybe disconnect a peer and discourage future connections from its address.
+ *
+ * @param[in] pnode The node to check.
+ * @return True if the peer was marked for disconnection in this function
+ */
bool PeerLogicValidation::MaybeDiscourageAndDisconnect(CNode& pnode)
{
- AssertLockHeld(cs_main);
- CNodeState &state = *State(pnode.GetId());
+ const NodeId peer_id{pnode.GetId()};
+ {
+ LOCK(cs_main);
+ CNodeState& state = *State(peer_id);
+
+ // There's nothing to do if the m_should_discourage flag isn't set
+ if (!state.m_should_discourage) return false;
- if (state.m_should_discourage) {
state.m_should_discourage = false;
- if (pnode.HasPermission(PF_NOBAN)) {
- LogPrintf("Warning: not punishing whitelisted peer %s!\n", pnode.addr.ToString());
- } else if (pnode.m_manual_connection) {
- LogPrintf("Warning: not punishing manually-connected peer %s!\n", pnode.addr.ToString());
- } else if (pnode.addr.IsLocal()) {
- // Disconnect but don't discourage this local node
- LogPrintf("Warning: disconnecting but not discouraging local peer %s!\n", pnode.addr.ToString());
- pnode.fDisconnect = true;
- } else {
- // Disconnect and discourage all nodes sharing the address
- LogPrintf("Disconnecting and discouraging peer %s!\n", pnode.addr.ToString());
- if (m_banman) {
- m_banman->Discourage(pnode.addr);
- }
- connman->DisconnectNode(pnode.addr);
- }
+ } // cs_main
+
+ if (pnode.HasPermission(PF_NOBAN)) {
+ // We never disconnect or discourage peers for bad behavior if they have the NOBAN permission flag
+ LogPrintf("Warning: not punishing noban peer %d!\n", peer_id);
+ return false;
+ }
+
+ if (pnode.IsManualConn()) {
+ // We never disconnect or discourage manual peers for bad behavior
+ LogPrintf("Warning: not punishing manually connected peer %d!\n", peer_id);
+ return false;
+ }
+
+ if (pnode.addr.IsLocal()) {
+ // We disconnect local peers for bad behavior but don't discourage (since that would discourage
+ // all peers on the same local address)
+ LogPrintf("Warning: disconnecting but not discouraging local peer %d!\n", peer_id);
+ pnode.fDisconnect = true;
return true;
}
- return false;
+
+ // Normal case: Disconnect the peer and discourage all nodes sharing the address
+ LogPrintf("Disconnecting and discouraging peer %d!\n", peer_id);
+ if (m_banman) m_banman->Discourage(pnode.addr);
+ m_connman.DisconnectNode(pnode.addr);
+ return true;
}
bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& interruptMsgProc)
@@ -3758,12 +3812,12 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
bool fMoreWork = false;
if (!pfrom->vRecvGetData.empty())
- ProcessGetData(*pfrom, chainparams, *connman, m_mempool, interruptMsgProc);
+ ProcessGetData(*pfrom, chainparams, m_connman, m_mempool, interruptMsgProc);
if (!pfrom->orphan_work_set.empty()) {
std::list<CTransactionRef> removed_txn;
LOCK2(cs_main, g_cs_orphans);
- ProcessOrphanTx(*connman, m_mempool, pfrom->orphan_work_set, removed_txn);
+ ProcessOrphanTx(m_connman, m_mempool, pfrom->orphan_work_set, removed_txn);
for (const CTransactionRef& removedTx : removed_txn) {
AddToCompactExtraTransactions(removedTx);
}
@@ -3789,7 +3843,7 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
// Just take one message
msgs.splice(msgs.begin(), pfrom->vProcessMsg, pfrom->vProcessMsg.begin());
pfrom->nProcessQueueSize -= msgs.front().m_raw_message_size;
- pfrom->fPauseRecv = pfrom->nProcessQueueSize > connman->GetReceiveFloodSize();
+ pfrom->fPauseRecv = pfrom->nProcessQueueSize > m_connman.GetReceiveFloodSize();
fMoreWork = !pfrom->vProcessMsg.empty();
}
CNetMessage& msg(msgs.front());
@@ -3823,7 +3877,7 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
}
try {
- ProcessMessage(*pfrom, msg_type, vRecv, msg.m_time, chainparams, m_chainman, m_mempool, *connman, m_banman, interruptMsgProc);
+ ProcessMessage(*pfrom, msg_type, vRecv, msg.m_time, chainparams, interruptMsgProc);
if (interruptMsgProc)
return false;
if (!pfrom->vRecvGetData.empty())
@@ -3834,9 +3888,6 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
LogPrint(BCLog::NET, "%s(%s, %u bytes): Unknown exception caught\n", __func__, SanitizeString(msg_type), nMessageSize);
}
- LOCK(cs_main);
- MaybeDiscourageAndDisconnect(*pfrom);
-
return fMoreWork;
}
@@ -3847,7 +3898,7 @@ void PeerLogicValidation::ConsiderEviction(CNode& pto, int64_t time_in_seconds)
CNodeState &state = *State(pto.GetId());
const CNetMsgMaker msgMaker(pto.GetSendVersion());
- if (!state.m_chain_sync.m_protect && IsOutboundDisconnectionCandidate(pto) && state.fSyncStarted) {
+ if (!state.m_chain_sync.m_protect && pto.IsOutboundOrBlockRelayConn() && state.fSyncStarted) {
// This is an outbound peer subject to disconnection if they don't
// announce a block with as much work as the current tip within
// CHAIN_SYNC_TIMEOUT + HEADERS_RESPONSE_TIME seconds (note: if
@@ -3879,7 +3930,7 @@ void PeerLogicValidation::ConsiderEviction(CNode& pto, int64_t time_in_seconds)
} else {
assert(state.m_chain_sync.m_work_header);
LogPrint(BCLog::NET, "sending getheaders to outbound peer=%d to verify chain work (current best known block:%s, benchmark blockhash: %s)\n", pto.GetId(), state.pindexBestKnownBlock != nullptr ? state.pindexBestKnownBlock->GetBlockHash().ToString() : "<none>", state.m_chain_sync.m_work_header->GetBlockHash().ToString());
- connman->PushMessage(&pto, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(state.m_chain_sync.m_work_header->pprev), uint256()));
+ m_connman.PushMessage(&pto, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(state.m_chain_sync.m_work_header->pprev), uint256()));
state.m_chain_sync.m_sent_getheaders = true;
constexpr int64_t HEADERS_RESPONSE_TIME = 120; // 2 minutes
// Bump the timeout to allow a response, which could clear the timeout
@@ -3896,7 +3947,7 @@ void PeerLogicValidation::ConsiderEviction(CNode& pto, int64_t time_in_seconds)
void PeerLogicValidation::EvictExtraOutboundPeers(int64_t time_in_seconds)
{
// Check whether we have too many outbound peers
- int extra_peers = connman->GetExtraOutboundCount();
+ int extra_peers = m_connman.GetExtraOutboundCount();
if (extra_peers > 0) {
// If we have more outbound peers than we target, disconnect one.
// Pick the outbound peer that least recently announced
@@ -3905,11 +3956,11 @@ void PeerLogicValidation::EvictExtraOutboundPeers(int64_t time_in_seconds)
NodeId worst_peer = -1;
int64_t oldest_block_announcement = std::numeric_limits<int64_t>::max();
- connman->ForEachNode([&](CNode* pnode) {
+ m_connman.ForEachNode([&](CNode* pnode) {
AssertLockHeld(cs_main);
// Ignore non-outbound peers, or nodes marked for disconnect already
- if (!IsOutboundDisconnectionCandidate(*pnode) || pnode->fDisconnect) return;
+ if (!pnode->IsOutboundOrBlockRelayConn() || pnode->fDisconnect) return;
CNodeState *state = State(pnode->GetId());
if (state == nullptr) return; // shouldn't be possible, but just in case
// Don't evict our protected peers
@@ -3922,7 +3973,7 @@ void PeerLogicValidation::EvictExtraOutboundPeers(int64_t time_in_seconds)
}
});
if (worst_peer != -1) {
- bool disconnected = connman->ForNode(worst_peer, [&](CNode *pnode) {
+ bool disconnected = m_connman.ForNode(worst_peer, [&](CNode *pnode) {
AssertLockHeld(cs_main);
// Only disconnect a peer that has been connected to us for
@@ -3946,7 +3997,7 @@ void PeerLogicValidation::EvictExtraOutboundPeers(int64_t time_in_seconds)
// detected a stale tip. Don't try any more extra peers until
// we next detect a stale tip, to limit the load we put on the
// network from these extra connections.
- connman->SetTryNewOutboundPeer(false);
+ m_connman.SetTryNewOutboundPeer(false);
}
}
}
@@ -3956,8 +4007,6 @@ void PeerLogicValidation::CheckForStaleTipAndEvictPeers(const Consensus::Params
{
LOCK(cs_main);
- if (connman == nullptr) return;
-
int64_t time_in_seconds = GetTime();
EvictExtraOutboundPeers(time_in_seconds);
@@ -3965,11 +4014,11 @@ void PeerLogicValidation::CheckForStaleTipAndEvictPeers(const Consensus::Params
if (time_in_seconds > m_stale_tip_check_time) {
// Check whether our tip is stale, and if so, allow using an extra
// outbound peer
- if (!fImporting && !fReindex && connman->GetNetworkActive() && connman->GetUseAddrmanOutgoing() && TipMayBeStale(consensusParams)) {
+ if (!fImporting && !fReindex && m_connman.GetNetworkActive() && m_connman.GetUseAddrmanOutgoing() && TipMayBeStale(consensusParams)) {
LogPrintf("Potential stale tip detected, will try using extra outbound peer (last tip update: %d seconds ago)\n", time_in_seconds - g_last_tip_update);
- connman->SetTryNewOutboundPeer(true);
- } else if (connman->GetTryNewOutboundPeer()) {
- connman->SetTryNewOutboundPeer(false);
+ m_connman.SetTryNewOutboundPeer(true);
+ } else if (m_connman.GetTryNewOutboundPeer()) {
+ m_connman.SetTryNewOutboundPeer(false);
}
m_stale_tip_check_time = time_in_seconds + STALE_CHECK_INTERVAL;
}
@@ -3999,48 +4048,49 @@ public:
bool PeerLogicValidation::SendMessages(CNode* pto)
{
const Consensus::Params& consensusParams = Params().GetConsensus();
- {
- // Don't send anything until the version handshake is complete
- if (!pto->fSuccessfullyConnected || pto->fDisconnect)
- return true;
- // If we get here, the outgoing message serialization version is set and can't change.
- const CNetMsgMaker msgMaker(pto->GetSendVersion());
+ // We must call MaybeDiscourageAndDisconnect first, to ensure that we'll
+ // disconnect misbehaving peers even before the version handshake is complete.
+ if (MaybeDiscourageAndDisconnect(*pto)) return true;
- //
- // Message: ping
- //
- bool pingSend = false;
- if (pto->fPingQueued) {
- // RPC ping request by user
- pingSend = true;
- }
- if (pto->nPingNonceSent == 0 && pto->m_ping_start.load() + PING_INTERVAL < GetTime<std::chrono::microseconds>()) {
- // Ping automatically sent as a latency probe & keepalive.
- pingSend = true;
- }
- if (pingSend) {
- uint64_t nonce = 0;
- while (nonce == 0) {
- GetRandBytes((unsigned char*)&nonce, sizeof(nonce));
- }
- pto->fPingQueued = false;
- pto->m_ping_start = GetTime<std::chrono::microseconds>();
- if (pto->nVersion > BIP0031_VERSION) {
- pto->nPingNonceSent = nonce;
- connman->PushMessage(pto, msgMaker.Make(NetMsgType::PING, nonce));
- } else {
- // Peer is too old to support ping command with nonce, pong will never arrive.
- pto->nPingNonceSent = 0;
- connman->PushMessage(pto, msgMaker.Make(NetMsgType::PING));
- }
- }
+ // Don't send anything until the version handshake is complete
+ if (!pto->fSuccessfullyConnected || pto->fDisconnect)
+ return true;
- TRY_LOCK(cs_main, lockMain);
- if (!lockMain)
- return true;
+ // If we get here, the outgoing message serialization version is set and can't change.
+ const CNetMsgMaker msgMaker(pto->GetSendVersion());
- if (MaybeDiscourageAndDisconnect(*pto)) return true;
+ //
+ // Message: ping
+ //
+ bool pingSend = false;
+ if (pto->fPingQueued) {
+ // RPC ping request by user
+ pingSend = true;
+ }
+ if (pto->nPingNonceSent == 0 && pto->m_ping_start.load() + PING_INTERVAL < GetTime<std::chrono::microseconds>()) {
+ // Ping automatically sent as a latency probe & keepalive.
+ pingSend = true;
+ }
+ if (pingSend) {
+ uint64_t nonce = 0;
+ while (nonce == 0) {
+ GetRandBytes((unsigned char*)&nonce, sizeof(nonce));
+ }
+ pto->fPingQueued = false;
+ pto->m_ping_start = GetTime<std::chrono::microseconds>();
+ if (pto->nVersion > BIP0031_VERSION) {
+ pto->nPingNonceSent = nonce;
+ m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::PING, nonce));
+ } else {
+ // Peer is too old to support ping command with nonce, pong will never arrive.
+ pto->nPingNonceSent = 0;
+ m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::PING));
+ }
+ }
+
+ {
+ LOCK(cs_main);
CNodeState &state = *State(pto->GetId());
@@ -4067,17 +4117,17 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
{
pto->m_addr_known->insert(addr.GetKey());
vAddr.push_back(addr);
- // receiver rejects addr messages larger than 1000
- if (vAddr.size() >= 1000)
+ // receiver rejects addr messages larger than MAX_ADDR_TO_SEND
+ if (vAddr.size() >= MAX_ADDR_TO_SEND)
{
- connman->PushMessage(pto, msgMaker.Make(NetMsgType::ADDR, vAddr));
+ m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::ADDR, vAddr));
vAddr.clear();
}
}
}
pto->vAddrToSend.clear();
if (!vAddr.empty())
- connman->PushMessage(pto, msgMaker.Make(NetMsgType::ADDR, vAddr));
+ m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::ADDR, vAddr));
// we only send the big addr message once
if (pto->vAddrToSend.capacity() > 40)
pto->vAddrToSend.shrink_to_fit();
@@ -4086,7 +4136,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
// Start block sync
if (pindexBestHeader == nullptr)
pindexBestHeader = ::ChainActive().Tip();
- bool fFetch = state.fPreferredDownload || (nPreferredDownload == 0 && !pto->fClient && !pto->fOneShot); // Download if this is a nice peer, or we have no nice peers and this one might do.
+ bool fFetch = state.fPreferredDownload || (nPreferredDownload == 0 && !pto->fClient && !pto->IsAddrFetchConn()); // Download if this is a nice peer, or we have no nice peers and this one might do.
if (!state.fSyncStarted && !pto->fClient && !fImporting && !fReindex) {
// Only actively request headers from a single peer, unless we're close to today.
if ((nSyncStarted == 0 && fFetch) || pindexBestHeader->GetBlockTime() > GetAdjustedTime() - 24 * 60 * 60) {
@@ -4104,7 +4154,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
if (pindexStart->pprev)
pindexStart = pindexStart->pprev;
LogPrint(BCLog::NET, "initial getheaders (%d) to peer=%d (startheight:%d)\n", pindexStart->nHeight, pto->GetId(), pto->nStartingHeight);
- connman->PushMessage(pto, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexStart), uint256()));
+ m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexStart), uint256()));
}
}
@@ -4188,10 +4238,10 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
LOCK(cs_most_recent_block);
if (most_recent_block_hash == pBestIndex->GetBlockHash()) {
if (state.fWantsCmpctWitness || !fWitnessesPresentInMostRecentCompactBlock)
- connman->PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, *most_recent_compact_block));
+ m_connman.PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, *most_recent_compact_block));
else {
CBlockHeaderAndShortTxIDs cmpctblock(*most_recent_block, state.fWantsCmpctWitness);
- connman->PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock));
+ m_connman.PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock));
}
fGotBlockFromCache = true;
}
@@ -4201,7 +4251,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
bool ret = ReadBlockFromDisk(block, pBestIndex, consensusParams);
assert(ret);
CBlockHeaderAndShortTxIDs cmpctblock(block, state.fWantsCmpctWitness);
- connman->PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock));
+ m_connman.PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock));
}
state.pindexBestHeaderSent = pBestIndex;
} else if (state.fPreferHeaders) {
@@ -4214,7 +4264,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
LogPrint(BCLog::NET, "%s: sending header %s to peer=%d\n", __func__,
vHeaders.front().GetHash().ToString(), pto->GetId());
}
- connman->PushMessage(pto, msgMaker.Make(NetMsgType::HEADERS, vHeaders));
+ m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::HEADERS, vHeaders));
state.pindexBestHeaderSent = pBestIndex;
} else
fRevertToInv = true;
@@ -4259,7 +4309,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
for (const uint256& hash : pto->vInventoryBlockToSend) {
vInv.push_back(CInv(MSG_BLOCK, hash));
if (vInv.size() == MAX_INV_SZ) {
- connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
+ m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
vInv.clear();
}
}
@@ -4271,8 +4321,8 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
bool fSendTrickle = pto->HasPermission(PF_NOBAN);
if (pto->m_tx_relay->nNextInvSend < current_time) {
fSendTrickle = true;
- if (pto->fInbound) {
- pto->m_tx_relay->nNextInvSend = std::chrono::microseconds{connman->PoissonNextSendInbound(nNow, INVENTORY_BROADCAST_INTERVAL)};
+ if (pto->IsInboundConn()) {
+ pto->m_tx_relay->nNextInvSend = std::chrono::microseconds{m_connman.PoissonNextSendInbound(nNow, INVENTORY_BROADCAST_INTERVAL)};
} else {
// Use half the delay for outbound peers, as there is less privacy concern for them.
pto->m_tx_relay->nNextInvSend = PoissonNextSend(current_time, std::chrono::seconds{INVENTORY_BROADCAST_INTERVAL >> 1});
@@ -4312,7 +4362,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
// Responses to MEMPOOL requests bypass the m_recently_announced_invs filter.
vInv.push_back(inv);
if (vInv.size() == MAX_INV_SZ) {
- connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
+ m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
vInv.clear();
}
}
@@ -4346,6 +4396,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
std::set<uint256>::iterator it = vInvTx.back();
vInvTx.pop_back();
uint256 hash = *it;
+ CInv inv(state.m_wtxid_relay ? MSG_WTX : MSG_TX, hash);
// Remove it from the to-be-sent set
pto->m_tx_relay->setInventoryTxToSend.erase(it);
// Check if not in the filter already
@@ -4353,7 +4404,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
continue;
}
// Not in the mempool anymore? don't bother sending it.
- auto txinfo = m_mempool.info(hash, state.m_wtxid_relay);
+ auto txinfo = m_mempool.info(ToGenTxid(inv));
if (!txinfo.tx) {
continue;
}
@@ -4366,7 +4417,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
if (pto->m_tx_relay->pfilter && !pto->m_tx_relay->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue;
// Send
State(pto->GetId())->m_recently_announced_invs.insert(hash);
- vInv.push_back(CInv(state.m_wtxid_relay ? MSG_WTX : MSG_TX, hash));
+ vInv.push_back(inv);
nRelayedTransactions++;
{
// Expire old relay messages
@@ -4387,7 +4438,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
}
}
if (vInv.size() == MAX_INV_SZ) {
- connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
+ m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
vInv.clear();
}
pto->m_tx_relay->filterInventoryKnown.insert(hash);
@@ -4404,7 +4455,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
}
}
if (!vInv.empty())
- connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
+ m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
// Detect whether we're stalling
current_time = GetTime<std::chrono::microseconds>();
@@ -4519,24 +4570,24 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
auto& tx_process_time = state.m_tx_download.m_tx_process_time;
while (!tx_process_time.empty() && tx_process_time.begin()->first <= current_time && state.m_tx_download.m_tx_in_flight.size() < MAX_PEER_TX_IN_FLIGHT) {
- const uint256 txid = tx_process_time.begin()->second;
+ const GenTxid gtxid = tx_process_time.begin()->second;
// Erase this entry from tx_process_time (it may be added back for
// processing at a later time, see below)
tx_process_time.erase(tx_process_time.begin());
- CInv inv(state.m_wtxid_relay ? MSG_WTX : (MSG_TX | GetFetchFlags(*pto)), txid);
+ CInv inv(gtxid.IsWtxid() ? MSG_WTX : (MSG_TX | GetFetchFlags(*pto)), gtxid.GetHash());
if (!AlreadyHave(inv, m_mempool)) {
// If this transaction was last requested more than 1 minute ago,
// then request.
- const auto last_request_time = GetTxRequestTime(inv.hash);
+ const auto last_request_time = GetTxRequestTime(gtxid);
if (last_request_time <= current_time - GETDATA_TX_INTERVAL) {
LogPrint(BCLog::NET, "Requesting %s peer=%d\n", inv.ToString(), pto->GetId());
vGetData.push_back(inv);
if (vGetData.size() >= MAX_GETDATA_SZ) {
- connman->PushMessage(pto, msgMaker.Make(NetMsgType::GETDATA, vGetData));
+ m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETDATA, vGetData));
vGetData.clear();
}
- UpdateTxRequestTime(inv.hash, current_time);
- state.m_tx_download.m_tx_in_flight.emplace(inv.hash, current_time);
+ UpdateTxRequestTime(gtxid, current_time);
+ state.m_tx_download.m_tx_in_flight.emplace(gtxid.GetHash(), current_time);
} else {
// This transaction is in flight from someone else; queue
// up processing to happen after the download times out
@@ -4550,19 +4601,19 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
// would open us up to an attacker using inbound
// wtxid-relay to prevent us from requesting transactions
// from outbound txid-relay peers).
- const auto next_process_time = CalculateTxGetDataTime(txid, current_time, !state.fPreferredDownload, false);
- tx_process_time.emplace(next_process_time, txid);
+ const auto next_process_time = CalculateTxGetDataTime(gtxid, current_time, !state.fPreferredDownload, false);
+ tx_process_time.emplace(next_process_time, gtxid);
}
} else {
// We have already seen this transaction, no need to download.
- state.m_tx_download.m_tx_announced.erase(inv.hash);
- state.m_tx_download.m_tx_in_flight.erase(inv.hash);
+ state.m_tx_download.m_tx_announced.erase(gtxid.GetHash());
+ state.m_tx_download.m_tx_in_flight.erase(gtxid.GetHash());
}
}
if (!vGetData.empty())
- connman->PushMessage(pto, msgMaker.Make(NetMsgType::GETDATA, vGetData));
+ m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETDATA, vGetData));
//
// Message: feefilter
@@ -4590,7 +4641,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
// We always have a fee filter of at least minRelayTxFee
filterToSend = std::max(filterToSend, ::minRelayTxFee.GetFeePerK());
if (filterToSend != pto->m_tx_relay->lastSentFeeFilter) {
- connman->PushMessage(pto, msgMaker.Make(NetMsgType::FEEFILTER, filterToSend));
+ m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::FEEFILTER, filterToSend));
pto->m_tx_relay->lastSentFeeFilter = filterToSend;
}
pto->m_tx_relay->nextSendTimeFeeFilter = PoissonNextSend(timeNow, AVG_FEEFILTER_BROADCAST_INTERVAL);
@@ -4602,7 +4653,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
pto->m_tx_relay->nextSendTimeFeeFilter = timeNow + GetRandInt(MAX_FEEFILTER_CHANGE_DELAY) * 1000000;
}
}
- }
+ } // release cs_main
return true;
}