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.cpp474
1 files changed, 254 insertions, 220 deletions
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index d48745aef..089cf2bdb 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -13,10 +13,9 @@
#include <consensus/validation.h>
#include <hash.h>
#include <index/blockfilterindex.h>
-#include <validation.h>
#include <merkleblock.h>
-#include <netmessagemaker.h>
#include <netbase.h>
+#include <netmessagemaker.h>
#include <policy/fees.h>
#include <policy/policy.h>
#include <primitives/block.h>
@@ -26,22 +25,22 @@
#include <scheduler.h>
#include <tinyformat.h>
#include <txmempool.h>
-#include <util/system.h>
+#include <util/check.h> // For NDEBUG compile time check
#include <util/strencodings.h>
+#include <util/system.h>
+#include <validation.h>
#include <memory>
#include <typeinfo>
-#if defined(NDEBUG)
-# error "Bitcoin cannot be compiled without assertions."
-#endif
-
/** Expiration time for orphan transactions in seconds */
static constexpr int64_t ORPHAN_TX_EXPIRE_TIME = 20 * 60;
/** Minimum time between orphan transactions expire time checks in seconds */
static constexpr int64_t ORPHAN_TX_EXPIRE_INTERVAL = 5 * 60;
/** How long to cache transactions in mapRelay for normal relay */
-static constexpr std::chrono::seconds RELAY_TX_CACHE_TIME{15 * 60};
+static constexpr std::chrono::seconds RELAY_TX_CACHE_TIME = std::chrono::minutes{15};
+/** How long a transaction has to be in the mempool before it can unconditionally be relayed (even when not in mapRelay). */
+static constexpr std::chrono::seconds UNCONDITIONAL_RELAY_DELAY = std::chrono::minutes{2};
/** Headers download timeout expressed in microseconds
* Timeout = base + per_header * (expected number of headers) */
static constexpr int64_t HEADERS_DOWNLOAD_TIMEOUT_BASE = 15 * 60 * 1000000; // 15 minutes
@@ -66,8 +65,8 @@ static constexpr int STALE_RELAY_AGE_LIMIT = 30 * 24 * 60 * 60;
/// Age after which a block is considered historical for purposes of rate
/// limiting block relay. Set to one week, denominated in seconds.
static constexpr int HISTORICAL_BLOCK_AGE = 7 * 24 * 60 * 60;
-/** Time between pings automatically sent out for latency probing and keepalive (in seconds). */
-static const int PING_INTERVAL = 2 * 60;
+/** Time between pings automatically sent out for latency probing and keepalive */
+static constexpr std::chrono::minutes PING_INTERVAL{2};
/** The maximum number of entries in a locator */
static const unsigned int MAX_LOCATOR_SZ = 101;
/** The maximum number of entries in an 'inv' protocol message */
@@ -120,11 +119,20 @@ static constexpr std::chrono::hours AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL{24};
/** Average delay between peer address broadcasts */
static constexpr std::chrono::seconds AVG_ADDRESS_BROADCAST_INTERVAL{30};
/** Average delay between trickled inventory transmissions in seconds.
- * Blocks and whitelisted receivers bypass this, outbound peers get half this delay. */
+ * Blocks and peers with noban permission bypass this, outbound peers get half this delay. */
static const unsigned int INVENTORY_BROADCAST_INTERVAL = 5;
-/** Maximum number of inventory items to send per transmission.
+/** Maximum rate of inventory items to send per second.
* Limits the impact of low-fee transaction floods. */
-static constexpr unsigned int INVENTORY_BROADCAST_MAX = 7 * INVENTORY_BROADCAST_INTERVAL;
+static constexpr unsigned int INVENTORY_BROADCAST_PER_SECOND = 7;
+/** Maximum number of inventory items to send per transmission. */
+static constexpr unsigned int INVENTORY_BROADCAST_MAX = INVENTORY_BROADCAST_PER_SECOND * INVENTORY_BROADCAST_INTERVAL;
+/** The number of most recently announced transactions a peer can request. */
+static constexpr unsigned int INVENTORY_MAX_RECENT_RELAY = 3500;
+/** Verify that INVENTORY_MAX_RECENT_RELAY is enough to cache everything typically
+ * relayed before unconditional relay from the mempool kicks in. This is only a
+ * lower bound, and it should be larger to account for higher inv rate to outbound
+ * peers, and random variations in the broadcast mechanism. */
+static_assert(INVENTORY_MAX_RECENT_RELAY >= INVENTORY_BROADCAST_PER_SECOND * UNCONDITIONAL_RELAY_DELAY / std::chrono::seconds{1}, "INVENTORY_RELAY_MAX too low");
/** Average delay between feefilter broadcasts in seconds. */
static constexpr unsigned int AVG_FEEFILTER_BROADCAST_INTERVAL = 10 * 60;
/** Maximum feefilter broadcast delay after significant change. */
@@ -189,7 +197,7 @@ namespace {
* We use this to avoid requesting transactions that have already been
* confirnmed.
*/
- RecursiveMutex g_cs_recent_confirmed_transactions;
+ 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. */
@@ -252,8 +260,8 @@ struct CNodeState {
bool fCurrentlyConnected;
//! Accumulated misbehaviour score for this peer.
int nMisbehavior;
- //! Whether this peer should be disconnected and banned (unless whitelisted).
- bool fShouldBan;
+ //! Whether this peer should be disconnected and marked as discouraged (unless it has the noban permission).
+ bool m_should_discourage;
//! String name of this peer (debugging/logging purposes).
const std::string name;
//! The best known block we know this peer has announced.
@@ -398,13 +406,16 @@ struct CNodeState {
//! Whether this peer is a manual connection
bool m_is_manual_connection;
+ //! A rolling bloom filter of all announced tx CInvs to this peer.
+ CRollingBloomFilter m_recently_announced_invs = CRollingBloomFilter{INVENTORY_MAX_RECENT_RELAY, 0.000001};
+
CNodeState(CAddress addrIn, std::string addrNameIn, bool is_inbound, bool is_manual) :
address(addrIn), name(std::move(addrNameIn)), m_is_inbound(is_inbound),
m_is_manual_connection (is_manual)
{
fCurrentlyConnected = false;
nMisbehavior = 0;
- fShouldBan = false;
+ m_should_discourage = false;
pindexBestKnownBlock = nullptr;
hashLastUnknownBlock.SetNull();
pindexLastCommonBlock = nullptr;
@@ -425,6 +436,7 @@ struct CNodeState {
fSupportsDesiredCmpctVersion = false;
m_chain_sync = { 0, nullptr, false, false };
m_last_block_announcement = 0;
+ m_recently_announced_invs.reset();
}
};
@@ -1019,7 +1031,8 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans)
}
/**
- * Mark a misbehaving peer to be banned depending upon the value of `-banscore`.
+ * 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)
{
@@ -1031,18 +1044,17 @@ void Misbehaving(NodeId pnode, int howmuch, const std::string& message) EXCLUSIV
return;
state->nMisbehavior += howmuch;
- int banscore = gArgs.GetArg("-banscore", DEFAULT_BANSCORE_THRESHOLD);
std::string message_prefixed = message.empty() ? "" : (": " + message);
- if (state->nMisbehavior >= banscore && state->nMisbehavior - howmuch < banscore)
+ if (state->nMisbehavior >= DISCOURAGEMENT_THRESHOLD && state->nMisbehavior - howmuch < DISCOURAGEMENT_THRESHOLD)
{
- LogPrint(BCLog::NET, "%s: %s peer=%d (%d -> %d) BAN THRESHOLD EXCEEDED%s\n", __func__, state->name, pnode, state->nMisbehavior-howmuch, state->nMisbehavior, message_prefixed);
- state->fShouldBan = true;
+ 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);
+ 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);
}
/**
- * Potentially ban a node based on the contents of a BlockValidationState object
+ * Potentially mark a node discouraged based on the contents of a BlockValidationState object
*
* @param[in] via_compact_block this bool is passed in because net_processing should
* punish peers differently depending on whether the data was provided in a compact
@@ -1072,7 +1084,7 @@ static bool MaybePunishNodeForBlock(NodeId nodeid, const BlockValidationState& s
break;
}
- // Ban outbound (but not inbound) peers if on an invalid chain.
+ // Discourage outbound (but not inbound) peers if on an invalid chain.
// Exempt HB compact block peers and manual connections.
if (!via_compact_block && !node_state->m_is_inbound && !node_state->m_is_manual_connection) {
Misbehaving(nodeid, 100, message);
@@ -1107,7 +1119,7 @@ static bool MaybePunishNodeForBlock(NodeId nodeid, const BlockValidationState& s
}
/**
- * Potentially ban a node based on the contents of a TxValidationState object
+ * Potentially disconnect and discourage a node based on the contents of a TxValidationState object
*
* @return Returns true if the peer was punished (probably disconnected)
*/
@@ -1328,9 +1340,10 @@ 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) {
+ LOCK(pnode->cs_inventory);
if (nNewHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : 0)) {
for (const uint256& hash : reverse_iterate(vHashes)) {
- pnode->PushBlockHash(hash);
+ pnode->vBlockHashesToAnnounce.push_back(hash);
}
}
});
@@ -1339,7 +1352,7 @@ void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CB
}
/**
- * Handle invalid block rejection and consequent peer banning, maintain which
+ * Handle invalid block rejection and consequent peer discouragement, maintain which
* peers announce compact blocks.
*/
void PeerLogicValidation::BlockChecked(const CBlock& block, const BlockValidationState& state) {
@@ -1419,10 +1432,9 @@ bool static AlreadyHave(const CInv& inv, const CTxMemPool& mempool) EXCLUSIVE_LO
void RelayTransaction(const uint256& txid, const CConnman& connman)
{
- CInv inv(MSG_TX, txid);
- connman.ForEachNode([&inv](CNode* pnode)
+ connman.ForEachNode([&txid](CNode* pnode)
{
- pnode->PushInventory(inv);
+ pnode->PushTxInventory(txid);
});
}
@@ -1441,7 +1453,7 @@ static void RelayAddress(const CAddress& addr, bool fReachable, const CConnman&
assert(nRelayNodes <= best.size());
auto sortfunc = [&best, &hasher, nRelayNodes](CNode* pnode) {
- if (pnode->nVersion >= CADDR_TIME_VERSION && pnode->IsAddrRelayPeer()) {
+ if (pnode->IsAddrRelayPeer()) {
uint64_t hashKey = CSipHasher(hasher).Write(pnode->GetId()).Finalize();
for (unsigned int i = 0; i < nRelayNodes; i++) {
if (hashKey > best[i].first) {
@@ -1512,7 +1524,7 @@ void static ProcessGetBlockData(CNode& pfrom, const CChainParams& chainparams, c
if (send &&
connman->OutboundTargetReached(true) &&
(((pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() > HISTORICAL_BLOCK_AGE)) || inv.type == MSG_FILTERED_BLOCK) &&
- !pfrom.HasPermission(PF_NOBAN) // never disconnect nodes with the noban permission
+ !pfrom.HasPermission(PF_DOWNLOAD) // nodes with the download permission may exceed target
) {
LogPrint(BCLog::NET, "historical block serving limit reached, disconnect peer=%d\n", pfrom.GetId());
@@ -1608,7 +1620,7 @@ void static ProcessGetBlockData(CNode& pfrom, const CChainParams& chainparams, c
// Trigger the peer node to send a getblocks request for the next batch of inventory
if (inv.hash == pfrom.hashContinue)
{
- // Bypass PushInventory, this must send even if redundant,
+ // Send immediately. This must send even if redundant,
// and we want it right after the last block so they don't
// wait for other stuff first.
std::vector<CInv> vInv;
@@ -1620,30 +1632,28 @@ 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(CNode& peer, const uint256& txid, const std::chrono::seconds mempool_req, const std::chrono::seconds longlived_mempool_time) LOCKS_EXCLUDED(cs_main)
+CTransactionRef static FindTxForGetData(const CNode& peer, const uint256& txid, const std::chrono::seconds mempool_req, const std::chrono::seconds now) LOCKS_EXCLUDED(cs_main)
{
- // Check if the requested transaction is so recent that we're just
- // about to announce it to the peer; if so, they certainly shouldn't
- // know we already have it.
- {
- LOCK(peer.m_tx_relay->cs_tx_inventory);
- if (peer.m_tx_relay->setInventoryTxToSend.count(txid)) return {};
+ auto txinfo = mempool.info(txid);
+ 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
+ // unconditionally.
+ if ((mempool_req.count() && txinfo.m_time <= mempool_req) || txinfo.m_time <= now - UNCONDITIONAL_RELAY_DELAY) {
+ return std::move(txinfo.tx);
+ }
}
{
LOCK(cs_main);
- // Look up transaction in relay pool
- auto mi = mapRelay.find(txid);
- if (mi != mapRelay.end()) return mi->second;
- }
- auto txinfo = mempool.info(txid);
- if (txinfo.tx) {
- // To protect privacy, do not answer getdata using the mempool when
- // that TX couldn't have been INVed in reply to a MEMPOOL request,
- // or when it's too recent to have expired from mapRelay.
- if ((mempool_req.count() && txinfo.m_time <= mempool_req) || txinfo.m_time <= longlived_mempool_time) {
- return txinfo.tx;
+ // Otherwise, the transaction must have been announced recently.
+ if (State(peer.GetId())->m_recently_announced_invs.contains(txid)) {
+ // 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);
+ if (mi != mapRelay.end()) return mi->second;
}
}
@@ -1658,8 +1668,7 @@ void static ProcessGetData(CNode& pfrom, const CChainParams& chainparams, CConnm
std::vector<CInv> vNotFound;
const CNetMsgMaker msgMaker(pfrom.GetSendVersion());
- // mempool entries added before this time have likely expired from mapRelay
- const std::chrono::seconds longlived_mempool_time = GetTime<std::chrono::seconds>() - RELAY_TX_CACHE_TIME;
+ const std::chrono::seconds now = GetTime<std::chrono::seconds>();
// Get last mempool request time
const std::chrono::seconds mempool_req = pfrom.m_tx_relay != nullptr ? pfrom.m_tx_relay->m_last_mempool_req.load()
: std::chrono::seconds::min();
@@ -1680,11 +1689,22 @@ void static ProcessGetData(CNode& pfrom, const CChainParams& chainparams, CConnm
continue;
}
- CTransactionRef tx = FindTxForGetData(pfrom, inv.hash, mempool_req, longlived_mempool_time);
+ CTransactionRef tx = FindTxForGetData(pfrom, inv.hash, mempool_req, now);
if (tx) {
int nSendFlags = (inv.type == MSG_TX ? SERIALIZE_TRANSACTION_NO_WITNESS : 0);
connman->PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *tx));
mempool.RemoveUnbroadcastTx(inv.hash);
+ // 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);
+ }
+ }
+ }
} else {
vNotFound.push_back(inv);
}
@@ -1746,14 +1766,14 @@ inline void static SendBlockTransactions(const CBlock& block, const BlockTransac
connman->PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCKTXN, resp));
}
-bool static ProcessHeadersMessage(CNode& pfrom, CConnman* connman, ChainstateManager& chainman, CTxMemPool& mempool, const std::vector<CBlockHeader>& headers, const CChainParams& chainparams, bool via_compact_block)
+static void ProcessHeadersMessage(CNode& pfrom, CConnman* connman, ChainstateManager& chainman, CTxMemPool& mempool, const std::vector<CBlockHeader>& headers, const CChainParams& chainparams, bool via_compact_block)
{
const CNetMsgMaker msgMaker(pfrom.GetSendVersion());
size_t nCount = headers.size();
if (nCount == 0) {
// Nothing interesting. Stop asking this peers for more headers.
- return true;
+ return;
}
bool received_new_header = false;
@@ -1786,14 +1806,14 @@ bool static ProcessHeadersMessage(CNode& pfrom, CConnman* connman, ChainstateMan
if (nodestate->nUnconnectingHeaders % MAX_UNCONNECTING_HEADERS == 0) {
Misbehaving(pfrom.GetId(), 20);
}
- return true;
+ return;
}
uint256 hashLastBlock;
for (const CBlockHeader& header : headers) {
if (!hashLastBlock.IsNull() && header.hashPrevBlock != hashLastBlock) {
Misbehaving(pfrom.GetId(), 20, "non-continuous headers sequence");
- return false;
+ return;
}
hashLastBlock = header.GetHash();
}
@@ -1809,7 +1829,7 @@ bool static ProcessHeadersMessage(CNode& pfrom, CConnman* connman, ChainstateMan
if (!chainman.ProcessNewBlockHeaders(headers, state, chainparams, &pindexLast)) {
if (state.IsInvalid()) {
MaybePunishNodeForBlock(pfrom.GetId(), state, via_compact_block, "invalid header received");
- return false;
+ return;
}
}
@@ -1898,8 +1918,8 @@ bool static ProcessHeadersMessage(CNode& pfrom, CConnman* connman, ChainstateMan
// headers to fetch from this peer.
if (nodestate->pindexBestKnownBlock && nodestate->pindexBestKnownBlock->nChainWork < nMinimumChainWork) {
// This peer has too little work on their headers chain to help
- // us sync -- disconnect if using an outbound slot (unless
- // whitelisted or addnode).
+ // us sync -- disconnect if it is an outbound disconnection
+ // candidate.
// Note: We compare their tip to nMinimumChainWork (rather than
// ::ChainActive().Tip()) because we won't start block download
// until we have a headers chain that has at least
@@ -1925,7 +1945,7 @@ bool static ProcessHeadersMessage(CNode& pfrom, CConnman* connman, ChainstateMan
}
}
- return true;
+ return;
}
void static ProcessOrphanTx(CConnman* connman, CTxMemPool& mempool, std::set<uint256>& orphan_work_set, std::list<CTransactionRef>& removed_txn) EXCLUSIVE_LOCKS_REQUIRED(cs_main, g_cs_orphans)
@@ -1969,7 +1989,10 @@ void static ProcessOrphanTx(CConnman* connman, CTxMemPool& mempool, std::set<uin
if (MaybePunishNodeForTx(fromPeer, orphan_state)) {
setMisbehaving.insert(fromPeer);
}
- LogPrint(BCLog::MEMPOOL, " invalid orphan tx %s\n", orphanHash.ToString());
+ LogPrint(BCLog::MEMPOOL, " invalid orphan tx %s from peer=%d. %s\n",
+ orphanHash.ToString(),
+ fromPeer,
+ orphan_state.ToString());
}
// Has inputs but not accepted to mempool
// Probably non-standard or insufficient fee
@@ -2205,37 +2228,33 @@ static void ProcessGetCFCheckPt(CNode& pfrom, CDataStream& vRecv, const CChainPa
connman.PushMessage(&pfrom, std::move(msg));
}
-bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams, ChainstateManager& chainman, CTxMemPool& mempool, CConnman* connman, BanMan* banman, const std::atomic<bool>& interruptMsgProc)
+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)
{
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)
{
LogPrintf("dropmessagestest DROPPING RECV MESSAGE\n");
- return true;
+ return;
}
- if (!(pfrom.GetLocalServices() & NODE_BLOOM) &&
- (msg_type == NetMsgType::FILTERLOAD ||
- msg_type == NetMsgType::FILTERADD))
- {
- if (pfrom.nVersion >= NO_BLOOM_VERSION) {
- LOCK(cs_main);
- Misbehaving(pfrom.GetId(), 100);
- return false;
- } else {
- pfrom.fDisconnect = true;
- return false;
- }
- }
-
if (msg_type == NetMsgType::VERSION) {
// Each connection can only send one version message
if (pfrom.nVersion != 0)
{
LOCK(cs_main);
Misbehaving(pfrom.GetId(), 1);
- return false;
+ return;
}
int64_t nTime;
@@ -2261,14 +2280,14 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
{
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;
- return false;
+ return;
}
if (nVersion < MIN_PEER_PROTO_VERSION) {
// disconnect from peers older than this proto version
LogPrint(BCLog::NET, "peer=%d using obsolete version %i; disconnecting\n", pfrom.GetId(), nVersion);
pfrom.fDisconnect = true;
- return false;
+ return;
}
if (!vRecv.empty())
@@ -2288,7 +2307,7 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
{
LogPrintf("connected to self at %s, disconnecting\n", pfrom.addr.ToString());
pfrom.fDisconnect = true;
- return true;
+ return;
}
if (pfrom.fInbound && addrMe.IsRoutable())
@@ -2356,11 +2375,8 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
}
// Get recent addresses
- if (pfrom.fOneShot || pfrom.nVersion >= CADDR_TIME_VERSION || connman->GetAddressCount() < 1000)
- {
- connman->PushMessage(&pfrom, CNetMsgMaker(nSendVersion).Make(NetMsgType::GETADDR));
- pfrom.fGetAddr = true;
- }
+ connman->PushMessage(&pfrom, CNetMsgMaker(nSendVersion).Make(NetMsgType::GETADDR));
+ pfrom.fGetAddr = true;
connman->MarkAddressGood(pfrom.addr);
}
@@ -2388,14 +2404,14 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
assert(pfrom.fInbound == false);
pfrom.fDisconnect = true;
}
- return true;
+ return;
}
if (pfrom.nVersion == 0) {
// Must have a version message before anything else
LOCK(cs_main);
Misbehaving(pfrom.GetId(), 1);
- return false;
+ return;
}
// At this point, the outgoing message serialization version can't change.
@@ -2436,31 +2452,28 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
connman->PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
}
pfrom.fSuccessfullyConnected = true;
- return true;
+ return;
}
if (!pfrom.fSuccessfullyConnected) {
// Must have a verack message before anything else
LOCK(cs_main);
Misbehaving(pfrom.GetId(), 1);
- return false;
+ return;
}
if (msg_type == NetMsgType::ADDR) {
std::vector<CAddress> vAddr;
vRecv >> vAddr;
- // Don't want addr from older versions unless seeding
- if (pfrom.nVersion < CADDR_TIME_VERSION && connman->GetAddressCount() > 1000)
- return true;
if (!pfrom.IsAddrRelayPeer()) {
- return true;
+ return;
}
if (vAddr.size() > 1000)
{
LOCK(cs_main);
- Misbehaving(pfrom.GetId(), 20, strprintf("message addr size() = %u", vAddr.size()));
- return false;
+ Misbehaving(pfrom.GetId(), 20, strprintf("addr message size = %u", vAddr.size()));
+ return;
}
// Store the new addresses
@@ -2470,7 +2483,7 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
for (CAddress& addr : vAddr)
{
if (interruptMsgProc)
- return true;
+ return;
// We only bother storing full nodes, though this may include
// things which we would not make an outbound connection to, in
@@ -2481,7 +2494,10 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60)
addr.nTime = nNow - 5 * 24 * 60 * 60;
pfrom.AddAddressKnown(addr);
- if (banman->IsBanned(addr)) continue; // Do not process banned addresses beyond remembering we received them
+ if (banman && (banman->IsDiscouraged(addr) || banman->IsBanned(addr))) {
+ // Do not process banned/discouraged addresses beyond remembering we received them
+ continue;
+ }
bool fReachable = IsReachable(addr);
if (addr.nTime > nSince && !pfrom.fGetAddr && vAddr.size() <= 10 && addr.IsRoutable())
{
@@ -2497,13 +2513,13 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
pfrom.fGetAddr = false;
if (pfrom.fOneShot)
pfrom.fDisconnect = true;
- return true;
+ return;
}
if (msg_type == NetMsgType::SENDHEADERS) {
LOCK(cs_main);
State(pfrom.GetId())->fPreferHeaders = true;
- return true;
+ return;
}
if (msg_type == NetMsgType::SENDCMPCT) {
@@ -2526,7 +2542,7 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
State(pfrom.GetId())->fSupportsDesiredCmpctVersion = (nCMPCTBLOCKVersion == 1);
}
}
- return true;
+ return;
}
if (msg_type == NetMsgType::INV) {
@@ -2535,17 +2551,18 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
if (vInv.size() > MAX_INV_SZ)
{
LOCK(cs_main);
- Misbehaving(pfrom.GetId(), 20, strprintf("message inv size() = %u", vInv.size()));
- return false;
+ Misbehaving(pfrom.GetId(), 20, strprintf("inv message size = %u", vInv.size()));
+ return;
}
// We won't accept tx inv's if we're in blocks-only mode, or this is a
// block-relay-only peer
bool fBlocksOnly = !g_relay_txes || (pfrom.m_tx_relay == nullptr);
- // Allow whitelisted peers to send data other than blocks in blocks only mode if whitelistrelay is true
- if (pfrom.HasPermission(PF_RELAY))
+ // Allow peers with relay permission to send data other than blocks in blocks only mode
+ if (pfrom.HasPermission(PF_RELAY)) {
fBlocksOnly = false;
+ }
LOCK(cs_main);
@@ -2556,7 +2573,7 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
for (CInv &inv : vInv)
{
if (interruptMsgProc)
- return true;
+ return;
bool fAlreadyHave = AlreadyHave(inv, mempool);
LogPrint(BCLog::NET, "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom.GetId());
@@ -2580,8 +2597,8 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
if (fBlocksOnly) {
LogPrint(BCLog::NET, "transaction (%s) inv sent in violation of protocol, disconnecting peer=%d\n", inv.hash.ToString(), pfrom.GetId());
pfrom.fDisconnect = true;
- return true;
- } else if (!fAlreadyHave && !fImporting && !fReindex && !::ChainstateActive().IsInitialBlockDownload()) {
+ return;
+ } else if (!fAlreadyHave && !chainman.ActiveChainstate().IsInitialBlockDownload()) {
RequestTx(State(pfrom.GetId()), inv.hash, current_time);
}
}
@@ -2592,7 +2609,7 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
LogPrint(BCLog::NET, "getheaders (%d) %s to peer=%d\n", pindexBestHeader->nHeight, best_block->ToString(), pfrom.GetId());
}
- return true;
+ return;
}
if (msg_type == NetMsgType::GETDATA) {
@@ -2601,8 +2618,8 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
if (vInv.size() > MAX_INV_SZ)
{
LOCK(cs_main);
- Misbehaving(pfrom.GetId(), 20, strprintf("message getdata size() = %u", vInv.size()));
- return false;
+ Misbehaving(pfrom.GetId(), 20, strprintf("getdata message size = %u", vInv.size()));
+ return;
}
LogPrint(BCLog::NET, "received getdata (%u invsz) peer=%d\n", vInv.size(), pfrom.GetId());
@@ -2613,7 +2630,7 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
pfrom.vRecvGetData.insert(pfrom.vRecvGetData.end(), vInv.begin(), vInv.end());
ProcessGetData(pfrom, chainparams, connman, mempool, interruptMsgProc);
- return true;
+ return;
}
if (msg_type == NetMsgType::GETBLOCKS) {
@@ -2624,7 +2641,7 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
if (locator.vHave.size() > MAX_LOCATOR_SZ) {
LogPrint(BCLog::NET, "getblocks locator size %lld > %d, disconnect peer=%d\n", locator.vHave.size(), MAX_LOCATOR_SZ, pfrom.GetId());
pfrom.fDisconnect = true;
- return true;
+ return;
}
// We might have announced the currently-being-connected tip using a
@@ -2671,7 +2688,7 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
LogPrint(BCLog::NET, " getblocks stopping, pruned or too old block at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
break;
}
- pfrom.PushInventory(CInv(MSG_BLOCK, pindex->GetBlockHash()));
+ WITH_LOCK(pfrom.cs_inventory, pfrom.vInventoryBlockToSend.push_back(pindex->GetBlockHash()));
if (--nLimit <= 0)
{
// When this block is requested, we'll send an inv that'll
@@ -2681,7 +2698,7 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
break;
}
}
- return true;
+ return;
}
if (msg_type == NetMsgType::GETBLOCKTXN) {
@@ -2697,7 +2714,7 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
}
if (recent_block) {
SendBlockTransactions(*recent_block, req, pfrom, connman);
- return true;
+ return;
}
LOCK(cs_main);
@@ -2705,7 +2722,7 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
const CBlockIndex* pindex = LookupBlockIndex(req.blockhash);
if (!pindex || !(pindex->nStatus & BLOCK_HAVE_DATA)) {
LogPrint(BCLog::NET, "Peer %d sent us a getblocktxn for a block we don't have\n", pfrom.GetId());
- return true;
+ return;
}
if (pindex->nHeight < ::ChainActive().Height() - MAX_BLOCKTXN_DEPTH) {
@@ -2722,7 +2739,7 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
inv.hash = req.blockhash;
pfrom.vRecvGetData.push_back(inv);
// The message processing loop will go around again (without pausing) and we'll respond then (without cs_main)
- return true;
+ return;
}
CBlock block;
@@ -2730,7 +2747,7 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
assert(ret);
SendBlockTransactions(block, req, pfrom, connman);
- return true;
+ return;
}
if (msg_type == NetMsgType::GETHEADERS) {
@@ -2741,13 +2758,13 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
if (locator.vHave.size() > MAX_LOCATOR_SZ) {
LogPrint(BCLog::NET, "getheaders locator size %lld > %d, disconnect peer=%d\n", locator.vHave.size(), MAX_LOCATOR_SZ, pfrom.GetId());
pfrom.fDisconnect = true;
- return true;
+ return;
}
LOCK(cs_main);
- if (::ChainstateActive().IsInitialBlockDownload() && !pfrom.HasPermission(PF_NOBAN)) {
+ if (::ChainstateActive().IsInitialBlockDownload() && !pfrom.HasPermission(PF_DOWNLOAD)) {
LogPrint(BCLog::NET, "Ignoring getheaders from peer=%d because node is in initial block download\n", pfrom.GetId());
- return true;
+ return;
}
CNodeState *nodestate = State(pfrom.GetId());
@@ -2757,12 +2774,12 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
// If locator is null, return the hashStop block
pindex = LookupBlockIndex(hashStop);
if (!pindex) {
- return true;
+ return;
}
if (!BlockRequestAllowed(pindex, chainparams.GetConsensus())) {
LogPrint(BCLog::NET, "%s: ignoring request from peer=%i for old block header that isn't in the main chain\n", __func__, pfrom.GetId());
- return true;
+ return;
}
}
else
@@ -2797,7 +2814,7 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
// in the SendMessages logic.
nodestate->pindexBestHeaderSent = pindex ? pindex : ::ChainActive().Tip();
connman->PushMessage(&pfrom, msgMaker.Make(NetMsgType::HEADERS, vHeaders));
- return true;
+ return;
}
if (msg_type == NetMsgType::TX) {
@@ -2808,7 +2825,7 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
{
LogPrint(BCLog::NET, "transaction sent in violation of protocol peer=%d\n", pfrom.GetId());
pfrom.fDisconnect = true;
- return true;
+ return;
}
CTransactionRef ptx;
@@ -2899,14 +2916,14 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
}
if (pfrom.HasPermission(PF_FORCERELAY)) {
- // Always relay transactions received from whitelisted peers, even
+ // Always relay transactions received from peers with forcerelay permission, even
// 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())) {
- LogPrintf("Not relaying non-mempool transaction %s from whitelisted peer=%d\n", tx.GetHash().ToString(), pfrom.GetId());
+ LogPrintf("Not relaying non-mempool transaction %s from forcerelay peer=%d\n", tx.GetHash().ToString(), pfrom.GetId());
} else {
- LogPrintf("Force relaying tx %s from whitelisted peer=%d\n", tx.GetHash().ToString(), pfrom.GetId());
+ LogPrintf("Force relaying tx %s from peer=%d\n", tx.GetHash().ToString(), pfrom.GetId());
RelayTransaction(tx.GetHash(), *connman);
}
}
@@ -2932,14 +2949,13 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
// peer simply for relaying a tx that our recentRejects has caught,
// regardless of false positives.
- if (state.IsInvalid())
- {
+ if (state.IsInvalid()) {
LogPrint(BCLog::MEMPOOLREJ, "%s from peer=%d was not accepted: %s\n", tx.GetHash().ToString(),
pfrom.GetId(),
state.ToString());
MaybePunishNodeForTx(pfrom.GetId(), state);
}
- return true;
+ return;
}
if (msg_type == NetMsgType::CMPCTBLOCK)
@@ -2947,7 +2963,7 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
// Ignore cmpctblock received while importing
if (fImporting || fReindex) {
LogPrint(BCLog::NET, "Unexpected cmpctblock message received from peer %d\n", pfrom.GetId());
- return true;
+ return;
}
CBlockHeaderAndShortTxIDs cmpctblock;
@@ -2962,7 +2978,7 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
// 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()));
- return true;
+ return;
}
if (!LookupBlockIndex(cmpctblock.header.GetHash())) {
@@ -2975,7 +2991,7 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
if (!chainman.ProcessNewBlockHeaders({cmpctblock.header}, state, chainparams, &pindex)) {
if (state.IsInvalid()) {
MaybePunishNodeForBlock(pfrom.GetId(), state, /*via_compact_block*/ true, "invalid header via cmpctblock");
- return true;
+ return;
}
}
@@ -3013,7 +3029,7 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
bool fAlreadyInFlight = blockInFlightIt != mapBlocksInFlight.end();
if (pindex->nStatus & BLOCK_HAVE_DATA) // Nothing to do here
- return true;
+ return;
if (pindex->nChainWork <= ::ChainActive().Tip()->nChainWork || // We know something better
pindex->nTx != 0) { // We had this block at some point, but pruned it
@@ -3024,17 +3040,17 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom), cmpctblock.header.GetHash());
connman->PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv));
}
- return true;
+ return;
}
// If we're not close to tip yet, give up and let parallel block fetch work its magic
if (!fAlreadyInFlight && !CanDirectFetch(chainparams.GetConsensus()))
- return true;
+ return;
if (IsWitnessEnabled(pindex->pprev, chainparams.GetConsensus()) && !nodestate->fSupportsDesiredCmpctVersion) {
// Don't bother trying to process compact blocks from v1 peers
// after segwit activates.
- return true;
+ return;
}
// We want to be a bit conservative just to be extra careful about DoS
@@ -3049,22 +3065,22 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
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");
- return true;
+ return;
}
}
PartiallyDownloadedBlock& partialBlock = *(*queuedBlockIt)->partialBlock;
ReadStatus status = partialBlock.InitData(cmpctblock, vExtraTxnForCompact);
if (status == READ_STATUS_INVALID) {
- MarkBlockAsReceived(pindex->GetBlockHash()); // Reset in-flight state in case of whitelist
+ 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()));
- return true;
+ 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));
- return true;
+ return;
}
BlockTransactionsRequest req;
@@ -3092,7 +3108,7 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
ReadStatus status = tempBlock.InitData(cmpctblock, vExtraTxnForCompact);
if (status != READ_STATUS_OK) {
// TODO: don't ignore failures
- return true;
+ return;
}
std::vector<CTransactionRef> dummy;
status = tempBlock.FillBlock(*pblock, dummy);
@@ -3107,7 +3123,7 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
std::vector<CInv> vInv(1);
vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom), cmpctblock.header.GetHash());
connman->PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv));
- return true;
+ return;
} else {
// If this was an announce-cmpctblock, we want the same treatment as a header message
fRevertToHeaderProcessing = true;
@@ -3116,14 +3132,14 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
} // cs_main
if (fProcessBLOCKTXN)
- return ProcessMessage(pfrom, NetMsgType::BLOCKTXN, blockTxnMsg, nTimeReceived, chainparams, chainman, mempool, connman, banman, interruptMsgProc);
+ return ProcessMessage(pfrom, NetMsgType::BLOCKTXN, blockTxnMsg, time_received, chainparams, chainman, mempool, connman, banman, interruptMsgProc);
if (fRevertToHeaderProcessing) {
// Headers received from HB compact block peers are permitted to be
// relayed before full validation (see BIP 152), so we don't want to disconnect
// 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 banned.
+ // will be detected and the peer will be disconnected/discouraged.
return ProcessHeadersMessage(pfrom, connman, chainman, mempool, {cmpctblock.header}, chainparams, /*via_compact_block=*/true);
}
@@ -3160,7 +3176,7 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
MarkBlockAsReceived(pblock->GetHash());
}
}
- return true;
+ return;
}
if (msg_type == NetMsgType::BLOCKTXN)
@@ -3168,7 +3184,7 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
// Ignore blocktxn received while importing
if (fImporting || fReindex) {
LogPrint(BCLog::NET, "Unexpected blocktxn message received from peer %d\n", pfrom.GetId());
- return true;
+ return;
}
BlockTransactions resp;
@@ -3183,15 +3199,15 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
if (it == mapBlocksInFlight.end() || !it->second.second->partialBlock ||
it->second.first != pfrom.GetId()) {
LogPrint(BCLog::NET, "Peer %d sent us block transactions for block we weren't expecting\n", pfrom.GetId());
- return true;
+ return;
}
PartiallyDownloadedBlock& partialBlock = *it->second.second->partialBlock;
ReadStatus status = partialBlock.FillBlock(*pblock, resp.txn);
if (status == READ_STATUS_INVALID) {
- MarkBlockAsReceived(resp.blockhash); // Reset in-flight state in case of whitelist
+ 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()));
- return true;
+ return;
} else if (status == READ_STATUS_FAILED) {
// Might have collided, fall back to getdata now :(
std::vector<CInv> invs;
@@ -3209,7 +3225,7 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
// 3. the block is otherwise invalid (eg invalid coinbase,
// block is too big, too many legacy sigops, etc).
// So if CheckBlock failed, #3 is the only possibility.
- // Under BIP 152, we don't DoS-ban unless proof of work is
+ // Under BIP 152, we don't discourage the peer unless proof of work is
// invalid (we don't require all the stateless checks to have
// been run). This is handled below, so just treat this as
// though the block was successfully read, and rely on the
@@ -3242,7 +3258,7 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
mapBlockSource.erase(pblock->GetHash());
}
}
- return true;
+ return;
}
if (msg_type == NetMsgType::HEADERS)
@@ -3250,7 +3266,7 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
// Ignore headers received while importing
if (fImporting || fReindex) {
LogPrint(BCLog::NET, "Unexpected headers message received from peer %d\n", pfrom.GetId());
- return true;
+ return;
}
std::vector<CBlockHeader> headers;
@@ -3260,7 +3276,7 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
if (nCount > MAX_HEADERS_RESULTS) {
LOCK(cs_main);
Misbehaving(pfrom.GetId(), 20, strprintf("headers message size = %u", nCount));
- return false;
+ return;
}
headers.resize(nCount);
for (unsigned int n = 0; n < nCount; n++) {
@@ -3276,7 +3292,7 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
// Ignore block received while importing
if (fImporting || fReindex) {
LogPrint(BCLog::NET, "Unexpected block message received from peer %d\n", pfrom.GetId());
- return true;
+ return;
}
std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>();
@@ -3304,7 +3320,7 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
LOCK(cs_main);
mapBlockSource.erase(pblock->GetHash());
}
- return true;
+ return;
}
if (msg_type == NetMsgType::GETADDR) {
@@ -3315,18 +3331,18 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
// the getaddr message mitigates the attack.
if (!pfrom.fInbound) {
LogPrint(BCLog::NET, "Ignoring \"getaddr\" from outbound connection. peer=%d\n", pfrom.GetId());
- return true;
+ return;
}
if (!pfrom.IsAddrRelayPeer()) {
LogPrint(BCLog::NET, "Ignoring \"getaddr\" from block-relay-only connection. peer=%d\n", pfrom.GetId());
- return true;
+ return;
}
// Only send one GetAddr response per connection to reduce resource waste
// and discourage addr stamping of INV announcements.
if (pfrom.fSentAddr) {
LogPrint(BCLog::NET, "Ignoring repeated \"getaddr\". peer=%d\n", pfrom.GetId());
- return true;
+ return;
}
pfrom.fSentAddr = true;
@@ -3334,11 +3350,12 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
std::vector<CAddress> vAddr = connman->GetAddresses();
FastRandomContext insecure_rand;
for (const CAddress &addr : vAddr) {
- if (!banman->IsBanned(addr)) {
+ bool banned_or_discouraged = banman && (banman->IsDiscouraged(addr) || banman->IsBanned(addr));
+ if (!banned_or_discouraged) {
pfrom.PushAddress(addr, insecure_rand);
}
}
- return true;
+ return;
}
if (msg_type == NetMsgType::MEMPOOL) {
@@ -3349,7 +3366,7 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
LogPrint(BCLog::NET, "mempool request with bloom filters disabled, disconnect peer=%d\n", pfrom.GetId());
pfrom.fDisconnect = true;
}
- return true;
+ return;
}
if (connman->OutboundTargetReached(false) && !pfrom.HasPermission(PF_MEMPOOL))
@@ -3359,14 +3376,14 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
LogPrint(BCLog::NET, "mempool request with bandwidth limit reached, disconnect peer=%d\n", pfrom.GetId());
pfrom.fDisconnect = true;
}
- return true;
+ return;
}
if (pfrom.m_tx_relay != nullptr) {
LOCK(pfrom.m_tx_relay->cs_tx_inventory);
pfrom.m_tx_relay->fSendMempool = true;
}
- return true;
+ return;
}
if (msg_type == NetMsgType::PING) {
@@ -3387,11 +3404,11 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
// return very quickly.
connman->PushMessage(&pfrom, msgMaker.Make(NetMsgType::PONG, nonce));
}
- return true;
+ return;
}
if (msg_type == NetMsgType::PONG) {
- int64_t pingUsecEnd = nTimeReceived;
+ const auto ping_end = time_received;
uint64_t nonce = 0;
size_t nAvail = vRecv.in_avail();
bool bPingFinished = false;
@@ -3405,11 +3422,11 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
if (nonce == pfrom.nPingNonceSent) {
// Matching pong received, this ping is no longer outstanding
bPingFinished = true;
- int64_t pingUsecTime = pingUsecEnd - pfrom.nPingUsecStart;
- if (pingUsecTime > 0) {
+ const auto ping_time = ping_end - pfrom.m_ping_start.load();
+ if (ping_time.count() > 0) {
// Successful ping time measurement, replace previous
- pfrom.nPingUsecTime = pingUsecTime;
- pfrom.nMinPingUsecTime = std::min(pfrom.nMinPingUsecTime.load(), pingUsecTime);
+ pfrom.nPingUsecTime = count_microseconds(ping_time);
+ pfrom.nMinPingUsecTime = std::min(pfrom.nMinPingUsecTime.load(), count_microseconds(ping_time));
} else {
// This should never happen
sProblem = "Timing mishap";
@@ -3443,10 +3460,14 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
if (bPingFinished) {
pfrom.nPingNonceSent = 0;
}
- return true;
+ return;
}
if (msg_type == NetMsgType::FILTERLOAD) {
+ if (!(pfrom.GetLocalServices() & NODE_BLOOM)) {
+ pfrom.fDisconnect = true;
+ return;
+ }
CBloomFilter filter;
vRecv >> filter;
@@ -3462,10 +3483,14 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
pfrom.m_tx_relay->pfilter.reset(new CBloomFilter(filter));
pfrom.m_tx_relay->fRelayTxes = true;
}
- return true;
+ return;
}
if (msg_type == NetMsgType::FILTERADD) {
+ if (!(pfrom.GetLocalServices() & NODE_BLOOM)) {
+ pfrom.fDisconnect = true;
+ return;
+ }
std::vector<unsigned char> vData;
vRecv >> vData;
@@ -3486,19 +3511,21 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
LOCK(cs_main);
Misbehaving(pfrom.GetId(), 100);
}
- return true;
+ return;
}
if (msg_type == NetMsgType::FILTERCLEAR) {
+ if (!(pfrom.GetLocalServices() & NODE_BLOOM)) {
+ pfrom.fDisconnect = true;
+ return;
+ }
if (pfrom.m_tx_relay == nullptr) {
- return true;
+ return;
}
LOCK(pfrom.m_tx_relay->cs_filter);
- if (pfrom.GetLocalServices() & NODE_BLOOM) {
- pfrom.m_tx_relay->pfilter = nullptr;
- }
+ pfrom.m_tx_relay->pfilter = nullptr;
pfrom.m_tx_relay->fRelayTxes = true;
- return true;
+ return;
}
if (msg_type == NetMsgType::FEEFILTER) {
@@ -3511,22 +3538,22 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
}
LogPrint(BCLog::NET, "received: feefilter of %s from peer=%d\n", CFeeRate(newFeeFilter).ToString(), pfrom.GetId());
}
- return true;
+ return;
}
if (msg_type == NetMsgType::GETCFILTERS) {
ProcessGetCFilters(pfrom, vRecv, chainparams, *connman);
- return true;
+ return;
}
if (msg_type == NetMsgType::GETCFHEADERS) {
ProcessGetCFHeaders(pfrom, vRecv, chainparams, *connman);
- return true;
+ return;
}
if (msg_type == NetMsgType::GETCFCHECKPT) {
ProcessGetCFCheckPt(pfrom, vRecv, chainparams, *connman);
- return true;
+ return;
}
if (msg_type == NetMsgType::NOTFOUND) {
@@ -3551,33 +3578,34 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
}
}
}
- return true;
+ return;
}
// Ignore unknown commands for extensibility
LogPrint(BCLog::NET, "Unknown command \"%s\" from peer=%d\n", SanitizeString(msg_type), pfrom.GetId());
- return true;
+ return;
}
-bool PeerLogicValidation::CheckIfBanned(CNode& pnode)
+bool PeerLogicValidation::MaybeDiscourageAndDisconnect(CNode& pnode)
{
AssertLockHeld(cs_main);
CNodeState &state = *State(pnode.GetId());
- if (state.fShouldBan) {
- state.fShouldBan = false;
- if (pnode.HasPermission(PF_NOBAN))
+ 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)
+ } 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 ban _this_ local node
- LogPrintf("Warning: disconnecting but not banning local 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 ban all nodes sharing the address
+ // Disconnect and discourage all nodes sharing the address
+ LogPrintf("Disconnecting and discouraging peer %s!\n", pnode.addr.ToString());
if (m_banman) {
- m_banman->Ban(pnode.addr, BanReasonNodeMisbehaving);
+ m_banman->Discourage(pnode.addr);
}
connman->DisconnectNode(pnode.addr);
}
@@ -3664,11 +3692,8 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
return fMoreWork;
}
- // Process message
- bool fRet = false;
- try
- {
- fRet = ProcessMessage(*pfrom, msg_type, vRecv, msg.m_time, chainparams, m_chainman, m_mempool, connman, m_banman, interruptMsgProc);
+ try {
+ ProcessMessage(*pfrom, msg_type, vRecv, msg.m_time, chainparams, m_chainman, m_mempool, connman, m_banman, interruptMsgProc);
if (interruptMsgProc)
return false;
if (!pfrom->vRecvGetData.empty())
@@ -3679,12 +3704,8 @@ 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);
}
- if (!fRet) {
- LogPrint(BCLog::NET, "%s(%s, %u bytes) FAILED peer=%d\n", __func__, SanitizeString(msg_type), nMessageSize, pfrom->GetId());
- }
-
LOCK(cs_main);
- CheckIfBanned(*pfrom);
+ MaybeDiscourageAndDisconnect(*pfrom);
return fMoreWork;
}
@@ -3862,7 +3883,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
// RPC ping request by user
pingSend = true;
}
- if (pto->nPingNonceSent == 0 && pto->nPingUsecStart + PING_INTERVAL * 1000000 < GetTimeMicros()) {
+ 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;
}
@@ -3872,7 +3893,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
GetRandBytes((unsigned char*)&nonce, sizeof(nonce));
}
pto->fPingQueued = false;
- pto->nPingUsecStart = GetTimeMicros();
+ pto->m_ping_start = GetTime<std::chrono::microseconds>();
if (pto->nVersion > BIP0031_VERSION) {
pto->nPingNonceSent = nonce;
connman->PushMessage(pto, msgMaker.Make(NetMsgType::PING, nonce));
@@ -3887,7 +3908,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
if (!lockMain)
return true;
- if (CheckIfBanned(*pto)) return true;
+ if (MaybeDiscourageAndDisconnect(*pto)) return true;
CNodeState &state = *State(pto->GetId());
@@ -4085,7 +4106,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
// If the peer's chain has this block, don't inv it back.
if (!PeerHasHeader(&state, pindex)) {
- pto->PushInventory(CInv(MSG_BLOCK, hashToAnnounce));
+ pto->vInventoryBlockToSend.push_back(hashToAnnounce);
LogPrint(BCLog::NET, "%s: sending inv peer=%d hash=%s\n", __func__,
pto->GetId(), hashToAnnounce.ToString());
}
@@ -4156,6 +4177,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
if (!pto->m_tx_relay->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue;
}
pto->m_tx_relay->filterInventoryKnown.insert(hash);
+ // 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));
@@ -4209,6 +4231,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(MSG_TX, hash));
nRelayedTransactions++;
{
@@ -4266,9 +4289,9 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
// Check for headers sync timeouts
if (state.fSyncStarted && state.nHeadersSyncTimeout < std::numeric_limits<int64_t>::max()) {
// Detect whether this is a stalling initial-headers-sync peer
- if (pindexBestHeader->GetBlockTime() <= GetAdjustedTime() - 24*60*60) {
+ if (pindexBestHeader->GetBlockTime() <= GetAdjustedTime() - 24 * 60 * 60) {
if (nNow > state.nHeadersSyncTimeout && nSyncStarted == 1 && (nPreferredDownload - state.fPreferredDownload >= 1)) {
- // Disconnect a (non-whitelisted) peer if it is our only sync peer,
+ // Disconnect a peer (without the noban permission) if it is our only sync peer,
// and we have others we could be using instead.
// Note: If all our peers are inbound, then we won't
// disconnect our sync peer for stalling; we have bigger
@@ -4278,7 +4301,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
pto->fDisconnect = true;
return true;
} else {
- LogPrintf("Timeout downloading headers from whitelisted peer=%d, not disconnecting\n", pto->GetId());
+ LogPrintf("Timeout downloading headers from noban peer=%d, not disconnecting\n", pto->GetId());
// Reset the headers sync state so that we have a
// chance to try downloading from a different peer.
// Note: this will also result in at least one more
@@ -4389,15 +4412,26 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
//
// Message: feefilter
//
- // We don't want white listed peers to filter txs to us if we have -whitelistforcerelay
if (pto->m_tx_relay != nullptr && pto->nVersion >= FEEFILTER_VERSION && gArgs.GetBoolArg("-feefilter", DEFAULT_FEEFILTER) &&
- !pto->HasPermission(PF_FORCERELAY)) {
+ !pto->HasPermission(PF_FORCERELAY) // peers with the forcerelay permission should not filter txs to us
+ ) {
CAmount currentFilter = m_mempool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFeePerK();
int64_t timeNow = GetTimeMicros();
+ static FeeFilterRounder g_filter_rounder{CFeeRate{DEFAULT_MIN_RELAY_TX_FEE}};
+ if (m_chainman.ActiveChainstate().IsInitialBlockDownload()) {
+ // Received tx-inv messages are discarded when the active
+ // chainstate is in IBD, so tell the peer to not send them.
+ currentFilter = MAX_MONEY;
+ } else {
+ static const CAmount MAX_FILTER{g_filter_rounder.round(MAX_MONEY)};
+ if (pto->m_tx_relay->lastSentFeeFilter == MAX_FILTER) {
+ // Send the current filter if we sent MAX_FILTER previously
+ // and made it out of IBD.
+ pto->m_tx_relay->nextSendTimeFeeFilter = timeNow - 1;
+ }
+ }
if (timeNow > pto->m_tx_relay->nextSendTimeFeeFilter) {
- static CFeeRate default_feerate(DEFAULT_MIN_RELAY_TX_FEE);
- static FeeFilterRounder filterRounder(default_feerate);
- CAmount filterToSend = filterRounder.round(currentFilter);
+ CAmount filterToSend = g_filter_rounder.round(currentFilter);
// We always have a fee filter of at least minRelayTxFee
filterToSend = std::max(filterToSend, ::minRelayTxFee.GetFeePerK());
if (filterToSend != pto->m_tx_relay->lastSentFeeFilter) {