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.cpp1590
1 files changed, 922 insertions, 668 deletions
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 587b655d1c..7b83583e41 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 */
@@ -76,6 +75,8 @@ static const unsigned int MAX_INV_SZ = 50000;
static constexpr int32_t MAX_PEER_TX_IN_FLIGHT = 100;
/** Maximum number of announced transactions from a peer */
static constexpr int32_t MAX_PEER_TX_ANNOUNCEMENTS = 2 * MAX_INV_SZ;
+/** How many microseconds to delay requesting transactions via txids, if we have wtxid-relaying peers */
+static constexpr std::chrono::microseconds TXID_RELAY_DELAY{std::chrono::seconds{2}};
/** How many microseconds to delay requesting transactions from inbound peers */
static constexpr std::chrono::microseconds INBOUND_PEER_TX_DELAY{std::chrono::seconds{2}};
/** How long to wait (in microseconds) before downloading a transaction from an additional peer */
@@ -120,11 +121,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. */
@@ -133,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.
@@ -143,12 +155,10 @@ struct COrphanTx {
};
RecursiveMutex g_cs_orphans;
std::map<uint256, COrphanTx> mapOrphanTransactions GUARDED_BY(g_cs_orphans);
+std::map<uint256, std::map<uint256, COrphanTx>::iterator> g_orphans_by_wtxid GUARDED_BY(g_cs_orphans);
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. */
@@ -179,6 +189,21 @@ namespace {
* million to make it highly unlikely for users to have issues with this
* filter.
*
+ * 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
+ * attacks that could otherwise interfere with transaction relay from
+ * non-wtxidrelay peers. For communicating with wtxidrelay peers, having
+ * 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);
@@ -189,7 +214,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. */
@@ -210,13 +235,16 @@ namespace {
/** Number of peers from which we're downloading blocks. */
int nPeersWithValidatedDownloads GUARDED_BY(cs_main) = 0;
+ /** Number of peers with wtxid relay. */
+ int g_wtxid_relay_peers GUARDED_BY(cs_main) = 0;
+
/** Number of outbound peers with m_chain_sync.m_protect. */
int g_outbound_peers_with_protect_from_disconnect GUARDED_BY(cs_main) = 0;
/** 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. */
@@ -252,8 +280,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.
@@ -378,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;
@@ -398,13 +426,19 @@ 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};
+
+ //! Whether this peer relays txs via wtxid
+ bool m_wtxid_relay{false};
+
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 +459,7 @@ struct CNodeState {
fSupportsDesiredCmpctVersion = false;
m_chain_sync = { 0, nullptr, false, false };
m_last_block_announcement = 0;
+ m_recently_announced_invs.reset();
}
};
@@ -441,32 +476,32 @@ static CNodeState *State(NodeId pnode) EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
return &it->second;
}
-static void UpdatePreferredDownload(CNode* node, CNodeState* state) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+static void UpdatePreferredDownload(const CNode& node, CNodeState* state) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
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;
}
-static void PushNodeVersion(CNode *pnode, CConnman* connman, int64_t nTime)
+static void PushNodeVersion(CNode& pnode, CConnman& connman, int64_t nTime)
{
// Note that pnode->GetLocalServices() is a reflection of the local
// services we were offering when the CNode object was created for this
// peer.
- ServiceFlags nLocalNodeServices = pnode->GetLocalServices();
- uint64_t nonce = pnode->GetLocalNonce();
- int nNodeStartingHeight = pnode->GetMyStartingHeight();
- NodeId nodeid = pnode->GetId();
- CAddress addr = pnode->addr;
+ ServiceFlags nLocalNodeServices = pnode.GetLocalServices();
+ uint64_t nonce = pnode.GetLocalNonce();
+ int nNodeStartingHeight = pnode.GetMyStartingHeight();
+ NodeId nodeid = pnode.GetId();
+ CAddress addr = pnode.addr;
CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService(), addr.nServices));
CAddress addrMe = CAddress(CService(), nLocalNodeServices);
- connman->PushMessage(pnode, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERSION, PROTOCOL_VERSION, (uint64_t)nLocalNodeServices, nTime, addrYou, addrMe,
- nonce, strSubVersion, nNodeStartingHeight, ::g_relay_txes && pnode->m_tx_relay != nullptr));
+ connman.PushMessage(&pnode, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERSION, PROTOCOL_VERSION, (uint64_t)nLocalNodeServices, nTime, addrYou, addrMe,
+ nonce, strSubVersion, nNodeStartingHeight, ::g_relay_txes && pnode.m_tx_relay != nullptr));
if (fLogIPs) {
LogPrint(BCLog::NET, "send version message: version %d, blocks=%d, us=%s, them=%s, peer=%d\n", PROTOCOL_VERSION, nNodeStartingHeight, addrMe.ToString(), addrYou.ToString(), nodeid);
@@ -576,7 +611,7 @@ static void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) EXCLUSIV
* lNodesAnnouncingHeaderAndIDs, and keeping that list under a certain size by
* removing the first element if necessary.
*/
-static void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman* connman) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+static void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman& connman) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
AssertLockHeld(cs_main);
CNodeState* nodestate = State(nodeid);
@@ -592,20 +627,20 @@ static void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman* connma
return;
}
}
- connman->ForNode(nodeid, [connman](CNode* pfrom){
+ connman.ForNode(nodeid, [&connman](CNode* pfrom){
AssertLockHeld(cs_main);
uint64_t nCMPCTBLOCKVersion = (pfrom->GetLocalServices() & NODE_WITNESS) ? 2 : 1;
if (lNodesAnnouncingHeaderAndIDs.size() >= 3) {
// As per BIP152, we only get 3 of our peers to announce
// blocks using compact encodings.
- connman->ForNode(lNodesAnnouncingHeaderAndIDs.front(), [connman, nCMPCTBLOCKVersion](CNode* pnodeStop){
+ connman.ForNode(lNodesAnnouncingHeaderAndIDs.front(), [&connman, nCMPCTBLOCKVersion](CNode* pnodeStop){
AssertLockHeld(cs_main);
- connman->PushMessage(pnodeStop, CNetMsgMaker(pnodeStop->GetSendVersion()).Make(NetMsgType::SENDCMPCT, /*fAnnounceUsingCMPCTBLOCK=*/false, nCMPCTBLOCKVersion));
+ connman.PushMessage(pnodeStop, CNetMsgMaker(pnodeStop->GetSendVersion()).Make(NetMsgType::SENDCMPCT, /*fAnnounceUsingCMPCTBLOCK=*/false, nCMPCTBLOCKVersion));
return true;
});
lNodesAnnouncingHeaderAndIDs.pop_front();
}
- connman->PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::SENDCMPCT, /*fAnnounceUsingCMPCTBLOCK=*/true, nCMPCTBLOCKVersion));
+ connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::SENDCMPCT, /*fAnnounceUsingCMPCTBLOCK=*/true, nCMPCTBLOCKVersion));
lNodesAnnouncingHeaderAndIDs.push_back(pfrom->GetId());
return true;
});
@@ -724,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) 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;
@@ -764,26 +799,29 @@ std::chrono::microseconds CalculateTxGetDataTime(const uint256& txid, std::chron
// We delay processing announcements from inbound peers
if (use_inbound_delay) process_time += INBOUND_PEER_TX_DELAY;
+ // We delay processing announcements from peers that use txid-relay (instead of wtxid)
+ if (use_txid_delay) process_time += TXID_RELAY_DELAY;
+
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);
+ 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
@@ -797,35 +835,29 @@ 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, *connman, GetTime());
}
void PeerLogicValidation::ReattemptInitialBroadcast(CScheduler& scheduler) const
{
- std::set<uint256> unbroadcast_txids = m_mempool.GetUnbroadcastTxs();
+ std::map<uint256, uint256> unbroadcast_txids = m_mempool.GetUnbroadcastTxs();
- for (const uint256& txid : unbroadcast_txids) {
+ for (const auto& elem : unbroadcast_txids) {
// Sanity check: all unbroadcast txns should exist in the mempool
- if (m_mempool.exists(txid)) {
- RelayTransaction(txid, *connman);
+ if (m_mempool.exists(elem.first)) {
+ LOCK(cs_main);
+ RelayTransaction(elem.first, elem.second, *connman);
} else {
- m_mempool.RemoveUnbroadcastTx(txid, true);
+ m_mempool.RemoveUnbroadcastTx(elem.first, true);
}
}
@@ -857,6 +889,8 @@ void PeerLogicValidation::FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTim
assert(nPeersWithValidatedDownloads >= 0);
g_outbound_peers_with_protect_from_disconnect -= state->m_chain_sync.m_protect;
assert(g_outbound_peers_with_protect_from_disconnect >= 0);
+ g_wtxid_relay_peers -= state->m_wtxid_relay;
+ assert(g_wtxid_relay_peers >= 0);
mapNodeState.erase(nodeid);
@@ -866,6 +900,7 @@ void PeerLogicValidation::FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTim
assert(nPreferredDownload == 0);
assert(nPeersWithValidatedDownloads == 0);
assert(g_outbound_peers_with_protect_from_disconnect == 0);
+ assert(g_wtxid_relay_peers == 0);
}
LogPrint(BCLog::NET, "Cleared nodestate for peer=%d\n", nodeid);
}
@@ -924,6 +959,8 @@ bool AddOrphanTx(const CTransactionRef& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRE
auto ret = mapOrphanTransactions.emplace(hash, COrphanTx{tx, peer, GetTime() + ORPHAN_TX_EXPIRE_TIME, g_orphan_list.size()});
assert(ret.second);
g_orphan_list.push_back(ret.first);
+ // Allow for lookups in the orphan pool by wtxid, as well as txid
+ g_orphans_by_wtxid.emplace(tx->GetWitnessHash(), ret.first);
for (const CTxIn& txin : tx->vin) {
mapOrphanTransactionsByPrev[txin.prevout].insert(ret.first);
}
@@ -960,6 +997,7 @@ int static EraseOrphanTx(uint256 hash) EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans)
it_last->second.list_pos = old_pos;
}
g_orphan_list.pop_back();
+ g_orphans_by_wtxid.erase(it->second.tx->GetWitnessHash());
mapOrphanTransactions.erase(it);
return 1;
@@ -1019,30 +1057,29 @@ 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)
+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;
- int banscore = gArgs.GetArg("-banscore", DEFAULT_BANSCORE_THRESHOLD);
- std::string message_prefixed = message.empty() ? "" : (": " + message);
- if (state->nMisbehavior >= banscore && state->nMisbehavior - howmuch < banscore)
+ 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) BAN THRESHOLD EXCEEDED%s\n", __func__, state->name, pnode, state->nMisbehavior-howmuch, state->nMisbehavior, message_prefixed);
- state->fShouldBan = true;
- } else
- LogPrint(BCLog::NET, "%s: %s peer=%d (%d -> %d)%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, "Misbehaving: peer=%d (%d -> %d)%s\n", 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 +1109,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 +1144,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)
*/
@@ -1125,10 +1162,12 @@ 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:
case TxValidationResult::TX_WITNESS_MUTATED:
+ case TxValidationResult::TX_WITNESS_STRIPPED:
case TxValidationResult::TX_CONFLICT:
case TxValidationResult::TX_MEMPOOL_POLICY:
break;
@@ -1169,14 +1208,15 @@ PeerLogicValidation::PeerLogicValidation(CConnman* connmanIn, BanMan* banman, CS
recentRejects.reset(new CRollingBloomFilter(120000, 0.000001));
// Blocks don't typically have more than 4000 transactions, so this should
- // be at least six blocks (~1 hr) worth of transactions that we can store.
+ // be at least six blocks (~1 hr) worth of transactions that we can store,
+ // inserting both a txid and wtxid for every observed transaction.
// If the number of transactions appearing in a block goes up, or if we are
// seeing getdata requests more than an hour after initial announcement, we
// can increase this number.
// The false positive rate of 1/1M should come out to less than 1
// transaction per day that would be inadvertently ignored (which is the
// same probability that we have in the reject filter).
- g_recent_confirmed_transactions.reset(new CRollingBloomFilter(24000, 0.000001));
+ g_recent_confirmed_transactions.reset(new CRollingBloomFilter(48000, 0.000001));
const Consensus::Params& consensusParams = Params().GetConsensus();
// Stale tip checking and peer eviction are on two different timers, but we
@@ -1232,6 +1272,9 @@ void PeerLogicValidation::BlockConnected(const std::shared_ptr<const CBlock>& pb
LOCK(g_cs_recent_confirmed_transactions);
for (const auto& ptx : pblock->vtx) {
g_recent_confirmed_transactions->insert(ptx->GetHash());
+ if (ptx->GetHash() != ptx->GetWitnessHash()) {
+ g_recent_confirmed_transactions->insert(ptx->GetWitnessHash());
+ }
}
}
}
@@ -1328,9 +1371,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 +1383,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) {
@@ -1365,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, *connman);
}
}
if (it != mapBlockSource.end())
@@ -1384,6 +1428,7 @@ bool static AlreadyHave(const CInv& inv, const CTxMemPool& mempool) EXCLUSIVE_LO
{
case MSG_TX:
case MSG_WITNESS_TX:
+ case MSG_WTX:
{
assert(recentRejects);
if (::ChainActive().Tip()->GetBlockHash() != hashRecentRejectsChainTip)
@@ -1398,7 +1443,11 @@ bool static AlreadyHave(const CInv& inv, const CTxMemPool& mempool) EXCLUSIVE_LO
{
LOCK(g_cs_orphans);
- if (mapOrphanTransactions.count(inv.hash)) return true;
+ if (!inv.IsMsgWtx() && mapOrphanTransactions.count(inv.hash)) {
+ return true;
+ } else if (inv.IsMsgWtx() && g_orphans_by_wtxid.count(inv.hash)) {
+ return true;
+ }
}
{
@@ -1406,8 +1455,7 @@ bool static AlreadyHave(const CInv& inv, const CTxMemPool& mempool) EXCLUSIVE_LO
if (g_recent_confirmed_transactions->contains(inv.hash)) return true;
}
- return recentRejects->contains(inv.hash) ||
- mempool.exists(inv.hash);
+ return recentRejects->contains(inv.hash) || mempool.exists(ToGenTxid(inv));
}
case MSG_BLOCK:
case MSG_WITNESS_BLOCK:
@@ -1417,12 +1465,17 @@ bool static AlreadyHave(const CInv& inv, const CTxMemPool& mempool) EXCLUSIVE_LO
return true;
}
-void RelayTransaction(const uint256& txid, const CConnman& connman)
+void RelayTransaction(const uint256& txid, const uint256& wtxid, const CConnman& connman)
{
- CInv inv(MSG_TX, txid);
- connman.ForEachNode([&inv](CNode* pnode)
+ connman.ForEachNode([&txid, &wtxid](CNode* pnode)
{
- pnode->PushInventory(inv);
+ AssertLockHeld(cs_main);
+ CNodeState &state = *State(pnode->GetId());
+ if (state.m_wtxid_relay) {
+ pnode->PushTxInventory(wtxid);
+ } else {
+ pnode->PushTxInventory(txid);
+ }
});
}
@@ -1441,7 +1494,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) {
@@ -1462,7 +1515,7 @@ static void RelayAddress(const CAddress& addr, bool fReachable, const CConnman&
connman.ForEachNodeThen(std::move(sortfunc), std::move(pushfunc));
}
-void static ProcessGetBlockData(CNode* pfrom, const CChainParams& chainparams, const CInv& inv, CConnman* connman)
+void static ProcessGetBlockData(CNode& pfrom, const CChainParams& chainparams, const CInv& inv, CConnman& connman)
{
bool send = false;
std::shared_ptr<const CBlock> a_recent_block;
@@ -1504,28 +1557,30 @@ void static ProcessGetBlockData(CNode* pfrom, const CChainParams& chainparams, c
if (pindex) {
send = BlockRequestAllowed(pindex, consensusParams);
if (!send) {
- LogPrint(BCLog::NET, "%s: ignoring request from peer=%i for old block that isn't in the main chain\n", __func__, pfrom->GetId());
+ LogPrint(BCLog::NET, "%s: ignoring request from peer=%i for old block that isn't in the main chain\n", __func__, pfrom.GetId());
}
}
- const CNetMsgMaker msgMaker(pfrom->GetSendVersion());
+ const CNetMsgMaker msgMaker(pfrom.GetSendVersion());
// disconnect node in case we have reached the outbound limit for serving historical blocks
- // never disconnect whitelisted nodes
- if (send && connman->OutboundTargetReached(true) && ( ((pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() > HISTORICAL_BLOCK_AGE)) || inv.type == MSG_FILTERED_BLOCK) && !pfrom->HasPermission(PF_NOBAN))
- {
- LogPrint(BCLog::NET, "historical block serving limit reached, disconnect peer=%d\n", pfrom->GetId());
+ if (send &&
+ connman.OutboundTargetReached(true) &&
+ (((pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() > HISTORICAL_BLOCK_AGE)) || inv.type == MSG_FILTERED_BLOCK) &&
+ !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());
//disconnect node
- pfrom->fDisconnect = true;
+ pfrom.fDisconnect = true;
send = false;
}
// Avoid leaking prune-height by never sending blocks below the NODE_NETWORK_LIMITED threshold
- if (send && !pfrom->HasPermission(PF_NOBAN) && (
- (((pfrom->GetLocalServices() & NODE_NETWORK_LIMITED) == NODE_NETWORK_LIMITED) && ((pfrom->GetLocalServices() & NODE_NETWORK) != NODE_NETWORK) && (::ChainActive().Tip()->nHeight - pindex->nHeight > (int)NODE_NETWORK_LIMITED_MIN_BLOCKS + 2 /* add two blocks buffer extension for possible races */) )
+ if (send && !pfrom.HasPermission(PF_NOBAN) && (
+ (((pfrom.GetLocalServices() & NODE_NETWORK_LIMITED) == NODE_NETWORK_LIMITED) && ((pfrom.GetLocalServices() & NODE_NETWORK) != NODE_NETWORK) && (::ChainActive().Tip()->nHeight - pindex->nHeight > (int)NODE_NETWORK_LIMITED_MIN_BLOCKS + 2 /* add two blocks buffer extension for possible races */) )
)) {
- LogPrint(BCLog::NET, "Ignore block request below NODE_NETWORK_LIMITED threshold from peer=%d\n", pfrom->GetId());
+ LogPrint(BCLog::NET, "Ignore block request below NODE_NETWORK_LIMITED threshold from peer=%d\n", pfrom.GetId());
//disconnect node and prevent it from stalling (would otherwise wait for the missing block)
- pfrom->fDisconnect = true;
+ pfrom.fDisconnect = true;
send = false;
}
// Pruned nodes may have deleted the block, so check whether
@@ -1542,7 +1597,7 @@ void static ProcessGetBlockData(CNode* pfrom, const CChainParams& chainparams, c
if (!ReadRawBlockFromDisk(block_data, pindex, chainparams.MessageStart())) {
assert(!"cannot load block from disk");
}
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::BLOCK, MakeSpan(block_data)));
+ connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::BLOCK, MakeSpan(block_data)));
// Don't set pblock as we've sent the block
} else {
// Send block from disk
@@ -1553,22 +1608,22 @@ void static ProcessGetBlockData(CNode* pfrom, const CChainParams& chainparams, c
}
if (pblock) {
if (inv.type == MSG_BLOCK)
- connman->PushMessage(pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCK, *pblock));
+ connman.PushMessage(&pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCK, *pblock));
else if (inv.type == MSG_WITNESS_BLOCK)
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::BLOCK, *pblock));
+ connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::BLOCK, *pblock));
else if (inv.type == MSG_FILTERED_BLOCK)
{
bool sendMerkleBlock = false;
CMerkleBlock merkleBlock;
- if (pfrom->m_tx_relay != nullptr) {
- LOCK(pfrom->m_tx_relay->cs_filter);
- if (pfrom->m_tx_relay->pfilter) {
+ if (pfrom.m_tx_relay != nullptr) {
+ LOCK(pfrom.m_tx_relay->cs_filter);
+ if (pfrom.m_tx_relay->pfilter) {
sendMerkleBlock = true;
- merkleBlock = CMerkleBlock(*pblock, *pfrom->m_tx_relay->pfilter);
+ merkleBlock = CMerkleBlock(*pblock, *pfrom.m_tx_relay->pfilter);
}
}
if (sendMerkleBlock) {
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::MERKLEBLOCK, merkleBlock));
+ connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::MERKLEBLOCK, merkleBlock));
// CMerkleBlock just contains hashes, so also push any transactions in the block the client did not see
// This avoids hurting performance by pointlessly requiring a round-trip
// Note that there is currently no way for a node to request any single transactions we didn't send here -
@@ -1577,7 +1632,7 @@ void static ProcessGetBlockData(CNode* pfrom, const CChainParams& chainparams, c
// however we MUST always provide at least what the remote peer needs
typedef std::pair<unsigned int, uint256> PairType;
for (PairType& pair : merkleBlock.vMatchedTxn)
- connman->PushMessage(pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::TX, *pblock->vtx[pair.first]));
+ connman.PushMessage(&pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::TX, *pblock->vtx[pair.first]));
}
// else
// no response
@@ -1588,101 +1643,120 @@ void static ProcessGetBlockData(CNode* pfrom, const CChainParams& chainparams, c
// they won't have a useful mempool to match against a compact block,
// and we don't feel like constructing the object for them, so
// instead we respond with the full, non-compact block.
- bool fPeerWantsWitness = State(pfrom->GetId())->fWantsCmpctWitness;
+ bool fPeerWantsWitness = State(pfrom.GetId())->fWantsCmpctWitness;
int nSendFlags = fPeerWantsWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS;
if (CanDirectFetch(consensusParams) && pindex->nHeight >= ::ChainActive().Height() - MAX_CMPCTBLOCK_DEPTH) {
if ((fPeerWantsWitness || !fWitnessesPresentInARecentCompactBlock) && a_recent_compact_block && a_recent_compact_block->header.GetHash() == pindex->GetBlockHash()) {
- connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, *a_recent_compact_block));
+ connman.PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, *a_recent_compact_block));
} else {
CBlockHeaderAndShortTxIDs cmpctblock(*pblock, fPeerWantsWitness);
- connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock));
+ connman.PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock));
}
} else {
- connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCK, *pblock));
+ connman.PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCK, *pblock));
}
}
}
// Trigger the peer node to send a getblocks request for the next batch of inventory
- if (inv.hash == pfrom->hashContinue)
+ 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;
vInv.push_back(CInv(MSG_BLOCK, ::ChainActive().Tip()->GetBlockHash()));
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::INV, vInv));
- pfrom->hashContinue.SetNull();
+ connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::INV, vInv));
+ pfrom.hashContinue.SetNull();
}
}
}
//! 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 GenTxid& gtxid, 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(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
+ // 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(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(gtxid.GetHash());
+ if (mi != mapRelay.end()) return mi->second;
}
}
return {};
}
-void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnman* connman, CTxMemPool& mempool, const std::atomic<bool>& interruptMsgProc) LOCKS_EXCLUDED(cs_main)
+void static ProcessGetData(CNode& pfrom, const CChainParams& chainparams, CConnman& connman, CTxMemPool& mempool, const std::atomic<bool>& interruptMsgProc) LOCKS_EXCLUDED(cs_main)
{
AssertLockNotHeld(cs_main);
- std::deque<CInv>::iterator it = pfrom->vRecvGetData.begin();
+ std::deque<CInv>::iterator it = pfrom.vRecvGetData.begin();
std::vector<CInv> vNotFound;
- const CNetMsgMaker msgMaker(pfrom->GetSendVersion());
+ 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()
+ const std::chrono::seconds mempool_req = pfrom.m_tx_relay != nullptr ? pfrom.m_tx_relay->m_last_mempool_req.load()
: std::chrono::seconds::min();
// 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)) {
+ 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.
- if (pfrom->fPauseSend) break;
+ if (pfrom.fPauseSend) break;
const CInv &inv = *it++;
- if (pfrom->m_tx_relay == nullptr) {
+ if (pfrom.m_tx_relay == nullptr) {
// Ignore GETDATA requests for transactions from blocks-only peers.
continue;
}
- CTransactionRef tx = FindTxForGetData(pfrom, inv.hash, mempool_req, longlived_mempool_time);
+ CTransactionRef tx = FindTxForGetData(pfrom, ToGenTxid(inv), 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);
+ // WTX and WITNESS_TX imply we serialize with witness
+ 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.
+ 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);
}
@@ -1690,7 +1764,7 @@ void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnm
// Only process one BLOCK item per call, since they're uncommon and can be
// expensive to process.
- if (it != pfrom->vRecvGetData.end() && !pfrom->fPauseSend) {
+ if (it != pfrom.vRecvGetData.end() && !pfrom.fPauseSend) {
const CInv &inv = *it++;
if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_CMPCT_BLOCK || inv.type == MSG_WITNESS_BLOCK) {
ProcessGetBlockData(pfrom, chainparams, inv, connman);
@@ -1699,7 +1773,7 @@ void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnm
// and continue processing the queue on the next call.
}
- pfrom->vRecvGetData.erase(pfrom->vRecvGetData.begin(), it);
+ pfrom.vRecvGetData.erase(pfrom.vRecvGetData.begin(), it);
if (!vNotFound.empty()) {
// Let the peer know that we didn't find what it asked for, so it doesn't
@@ -1716,49 +1790,49 @@ void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnm
// In normal operation, we often send NOTFOUND messages for parents of
// transactions that we relay; if a peer is missing a parent, they may
// assume we have them and request the parents from us.
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::NOTFOUND, vNotFound));
+ connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::NOTFOUND, vNotFound));
}
}
-static uint32_t GetFetchFlags(CNode* pfrom) EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
+static uint32_t GetFetchFlags(const CNode& pfrom) EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
uint32_t nFetchFlags = 0;
- if ((pfrom->GetLocalServices() & NODE_WITNESS) && State(pfrom->GetId())->fHaveWitness) {
+ if ((pfrom.GetLocalServices() & NODE_WITNESS) && State(pfrom.GetId())->fHaveWitness) {
nFetchFlags |= MSG_WITNESS_FLAG;
}
return nFetchFlags;
}
-inline void static SendBlockTransactions(const CBlock& block, const BlockTransactionsRequest& req, CNode* pfrom, CConnman* connman) {
+inline void static SendBlockTransactions(const CBlock& block, const BlockTransactionsRequest& req, CNode& pfrom, CConnman& connman) {
BlockTransactions resp(req);
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]];
}
LOCK(cs_main);
- const CNetMsgMaker msgMaker(pfrom->GetSendVersion());
- int nSendFlags = State(pfrom->GetId())->fWantsCmpctWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS;
- connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCKTXN, resp));
+ const CNetMsgMaker msgMaker(pfrom.GetSendVersion());
+ int nSendFlags = State(pfrom.GetId())->fWantsCmpctWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS;
+ 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());
+ 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;
const CBlockIndex *pindexLast = nullptr;
{
LOCK(cs_main);
- CNodeState *nodestate = State(pfrom->GetId());
+ CNodeState *nodestate = State(pfrom.GetId());
// If this looks like it could be a block announcement (nCount <
// MAX_BLOCKS_TO_ANNOUNCE), use special logic for handling headers that
@@ -1770,28 +1844,28 @@ bool static ProcessHeadersMessage(CNode* pfrom, CConnman* connman, ChainstateMan
// nUnconnectingHeaders gets reset back to 0.
if (!LookupBlockIndex(headers[0].hashPrevBlock) && nCount < MAX_BLOCKS_TO_ANNOUNCE) {
nodestate->nUnconnectingHeaders++;
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), uint256()));
+ connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), uint256()));
LogPrint(BCLog::NET, "received header %s: missing prev block %s, sending getheaders (%d) to end (peer=%d, nUnconnectingHeaders=%d)\n",
headers[0].GetHash().ToString(),
headers[0].hashPrevBlock.ToString(),
pindexBestHeader->nHeight,
- pfrom->GetId(), nodestate->nUnconnectingHeaders);
+ pfrom.GetId(), nodestate->nUnconnectingHeaders);
// Set hashLastUnknownBlock for this peer, so that if we
// eventually get the headers - even from a different peer -
// we can use this peer to download.
- UpdateBlockAvailability(pfrom->GetId(), headers.back().GetHash());
+ 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 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;
+ Misbehaving(pfrom.GetId(), 20, "non-continuous headers sequence");
+ return;
}
hashLastBlock = header.GetHash();
}
@@ -1806,21 +1880,21 @@ bool static ProcessHeadersMessage(CNode* pfrom, CConnman* connman, ChainstateMan
BlockValidationState state;
if (!chainman.ProcessNewBlockHeaders(headers, state, chainparams, &pindexLast)) {
if (state.IsInvalid()) {
- MaybePunishNodeForBlock(pfrom->GetId(), state, via_compact_block, "invalid header received");
- return false;
+ MaybePunishNodeForBlock(pfrom.GetId(), state, via_compact_block, "invalid header received");
+ return;
}
}
{
LOCK(cs_main);
- CNodeState *nodestate = State(pfrom->GetId());
+ CNodeState *nodestate = State(pfrom.GetId());
if (nodestate->nUnconnectingHeaders > 0) {
- LogPrint(BCLog::NET, "peer=%d: resetting nUnconnectingHeaders (%d -> 0)\n", pfrom->GetId(), nodestate->nUnconnectingHeaders);
+ LogPrint(BCLog::NET, "peer=%d: resetting nUnconnectingHeaders (%d -> 0)\n", pfrom.GetId(), nodestate->nUnconnectingHeaders);
}
nodestate->nUnconnectingHeaders = 0;
assert(pindexLast);
- UpdateBlockAvailability(pfrom->GetId(), pindexLast->GetBlockHash());
+ UpdateBlockAvailability(pfrom.GetId(), pindexLast->GetBlockHash());
// From here, pindexBestKnownBlock should be guaranteed to be non-null,
// because it is set in UpdateBlockAvailability. Some nullptr checks
@@ -1834,8 +1908,8 @@ bool static ProcessHeadersMessage(CNode* pfrom, CConnman* connman, ChainstateMan
// Headers message had its maximum size; the peer may have more headers.
// TODO: optimize: if pindexLast is an ancestor of ::ChainActive().Tip or pindexBestHeader, continue
// from there instead.
- LogPrint(BCLog::NET, "more getheaders (%d) to end to peer=%d (startheight:%d)\n", pindexLast->nHeight, pfrom->GetId(), pfrom->nStartingHeight);
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexLast), uint256()));
+ LogPrint(BCLog::NET, "more getheaders (%d) to end to peer=%d (startheight:%d)\n", pindexLast->nHeight, pfrom.GetId(), pfrom.nStartingHeight);
+ connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexLast), uint256()));
}
bool fCanDirectFetch = CanDirectFetch(chainparams.GetConsensus());
@@ -1848,7 +1922,7 @@ bool static ProcessHeadersMessage(CNode* pfrom, CConnman* connman, ChainstateMan
while (pindexWalk && !::ChainActive().Contains(pindexWalk) && vToFetch.size() <= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
if (!(pindexWalk->nStatus & BLOCK_HAVE_DATA) &&
!mapBlocksInFlight.count(pindexWalk->GetBlockHash()) &&
- (!IsWitnessEnabled(pindexWalk->pprev, chainparams.GetConsensus()) || State(pfrom->GetId())->fHaveWitness)) {
+ (!IsWitnessEnabled(pindexWalk->pprev, chainparams.GetConsensus()) || State(pfrom.GetId())->fHaveWitness)) {
// We don't have this block, and it's not yet in flight.
vToFetch.push_back(pindexWalk);
}
@@ -1872,9 +1946,9 @@ bool static ProcessHeadersMessage(CNode* pfrom, CConnman* connman, ChainstateMan
}
uint32_t nFetchFlags = GetFetchFlags(pfrom);
vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash()));
- MarkBlockAsInFlight(mempool, pfrom->GetId(), pindex->GetBlockHash(), pindex);
+ MarkBlockAsInFlight(mempool, pfrom.GetId(), pindex->GetBlockHash(), pindex);
LogPrint(BCLog::NET, "Requesting block %s from peer=%d\n",
- pindex->GetBlockHash().ToString(), pfrom->GetId());
+ pindex->GetBlockHash().ToString(), pfrom.GetId());
}
if (vGetData.size() > 1) {
LogPrint(BCLog::NET, "Downloading blocks toward %s (%d) via headers direct fetch\n",
@@ -1885,7 +1959,7 @@ bool static ProcessHeadersMessage(CNode* pfrom, CConnman* connman, ChainstateMan
// In any case, we want to download using a compact block, not a regular one
vGetData[0] = CInv(MSG_CMPCT_BLOCK, vGetData[0].hash);
}
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vGetData));
+ connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, vGetData));
}
}
}
@@ -1896,37 +1970,37 @@ 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
// nMinimumChainWork, even if a peer has a chain past our tip,
// as an anti-DoS measure.
- if (IsOutboundDisconnectionCandidate(pfrom)) {
- LogPrintf("Disconnecting outbound peer %d -- headers chain has insufficient work\n", pfrom->GetId());
- pfrom->fDisconnect = true;
+ 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
// only consider setting m_protect for the full-relay peers.
if (g_outbound_peers_with_protect_from_disconnect < MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT && nodestate->pindexBestKnownBlock->nChainWork >= ::ChainActive().Tip()->nChainWork && !nodestate->m_chain_sync.m_protect) {
- LogPrint(BCLog::NET, "Protecting outbound peer=%d from eviction\n", pfrom->GetId());
+ LogPrint(BCLog::NET, "Protecting outbound peer=%d from eviction\n", pfrom.GetId());
nodestate->m_chain_sync.m_protect = true;
++g_outbound_peers_with_protect_from_disconnect;
}
}
}
- 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)
+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)
{
AssertLockHeld(cs_main);
AssertLockHeld(g_cs_orphans);
@@ -1950,7 +2024,7 @@ void static ProcessOrphanTx(CConnman* connman, CTxMemPool& mempool, std::set<uin
if (setMisbehaving.count(fromPeer)) continue;
if (AcceptToMemoryPool(mempool, orphan_state, porphanTx, &removed_txn, false /* bypass_limits */, 0 /* nAbsurdFee */)) {
LogPrint(BCLog::MEMPOOL, " accepted orphan tx %s\n", orphanHash.ToString());
- RelayTransaction(orphanHash, *connman);
+ RelayTransaction(orphanHash, porphanTx->GetWitnessHash(), connman);
for (unsigned int i = 0; i < orphanTx.vout.size(); i++) {
auto it_by_prev = mapOrphanTransactionsByPrev.find(COutPoint(orphanHash, i));
if (it_by_prev != mapOrphanTransactionsByPrev.end()) {
@@ -1967,17 +2041,43 @@ 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
LogPrint(BCLog::MEMPOOL, " removed orphan tx %s\n", orphanHash.ToString());
- if (!orphanTx.HasWitness() && orphan_state.GetResult() != TxValidationResult::TX_WITNESS_MUTATED) {
- // Do not use rejection cache for witness transactions or
- // witness-stripped transactions, as they can have been malleated.
- // See https://github.com/bitcoin/bitcoin/issues/8279 for details.
+ if (orphan_state.GetResult() != TxValidationResult::TX_WITNESS_STRIPPED) {
+ // We can add the wtxid of this transaction to our reject filter.
+ // Do not add txids of witness transactions or witness-stripped
+ // transactions to the filter, as they can have been malleated;
+ // adding such txids to the reject filter would potentially
+ // interfere with relay of valid transactions from peers that
+ // do not support wtxid-based relay. See
+ // https://github.com/bitcoin/bitcoin/issues/8279 for details.
+ // We can remove this restriction (and always add wtxids to
+ // the filter even for witness stripped transactions) once
+ // wtxid-based relay is broadly deployed.
+ // See also comments in https://github.com/bitcoin/bitcoin/pull/18044#discussion_r443419034
+ // for concerns around weakening security of unupgraded nodes
+ // if we start doing this too early.
assert(recentRejects);
- recentRejects->insert(orphanHash);
+ 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;
@@ -2202,37 +2302,33 @@ static void ProcessGetCFCheckPt(CNode& peer, CDataStream& vRecv, const CChainPar
connman.PushMessage(&peer, 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());
+ 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)
+ if (pfrom.nVersion != 0)
{
LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 1);
- return false;
+ Misbehaving(pfrom.GetId(), 1, "redundant version message");
+ return;
}
int64_t nTime;
@@ -2250,22 +2346,22 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
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);
+ 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;
- return false;
+ 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;
}
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;
+ LogPrint(BCLog::NET, "peer=%d using obsolete version %i; disconnecting\n", pfrom.GetId(), nVersion);
+ pfrom.fDisconnect = true;
+ return;
}
if (!vRecv.empty())
@@ -2281,145 +2377,145 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
if (!vRecv.empty())
vRecv >> fRelay;
// Disconnect if we connected to ourself
- if (pfrom->fInbound && !connman->CheckIncomingNonce(nNonce))
+ if (pfrom.IsInboundConn() && !connman.CheckIncomingNonce(nNonce))
{
- LogPrintf("connected to self at %s, disconnecting\n", pfrom->addr.ToString());
- pfrom->fDisconnect = true;
- return true;
+ 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)
+ if (pfrom.IsInboundConn())
PushNodeVersion(pfrom, connman, GetAdjustedTime());
- connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERACK));
+ if (nVersion >= WTXID_RELAY_VERSION) {
+ connman.PushMessage(&pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::WTXIDRELAY));
+ }
+
+ connman.PushMessage(&pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERACK));
- pfrom->nServices = nServices;
- pfrom->SetAddrLocal(addrMe);
+ pfrom.nServices = nServices;
+ pfrom.SetAddrLocal(addrMe);
{
- LOCK(pfrom->cs_SubVer);
- pfrom->cleanSubVer = cleanSubVer;
+ LOCK(pfrom.cs_SubVer);
+ pfrom.cleanSubVer = cleanSubVer;
}
- pfrom->nStartingHeight = nStartingHeight;
+ pfrom.nStartingHeight = nStartingHeight;
// set nodes not relaying blocks and tx and not serving (parts) of the historical blockchain as "clients"
- pfrom->fClient = (!(nServices & NODE_NETWORK) && !(nServices & NODE_NETWORK_LIMITED));
+ pfrom.fClient = (!(nServices & NODE_NETWORK) && !(nServices & NODE_NETWORK_LIMITED));
// set nodes not capable of serving the complete blockchain history as "limited nodes"
- pfrom->m_limited_node = (!(nServices & NODE_NETWORK) && (nServices & NODE_NETWORK_LIMITED));
+ pfrom.m_limited_node = (!(nServices & NODE_NETWORK) && (nServices & NODE_NETWORK_LIMITED));
- if (pfrom->m_tx_relay != nullptr) {
- LOCK(pfrom->m_tx_relay->cs_filter);
- pfrom->m_tx_relay->fRelayTxes = fRelay; // set to true after we get the first filter* message
+ if (pfrom.m_tx_relay != nullptr) {
+ LOCK(pfrom.m_tx_relay->cs_filter);
+ pfrom.m_tx_relay->fRelayTxes = fRelay; // set to true after we get the first filter* message
}
// Change version
- pfrom->SetSendVersion(nSendVersion);
- pfrom->nVersion = nVersion;
+ pfrom.SetSendVersion(nSendVersion);
+ pfrom.nVersion = nVersion;
if((nServices & NODE_WITNESS))
{
LOCK(cs_main);
- State(pfrom->GetId())->fHaveWitness = true;
+ State(pfrom.GetId())->fHaveWitness = true;
}
// Potentially mark this peer as a preferred download peer.
{
LOCK(cs_main);
- UpdatePreferredDownload(pfrom, State(pfrom->GetId()));
+ UpdatePreferredDownload(pfrom, State(pfrom.GetId()));
}
- if (!pfrom->fInbound && pfrom->IsAddrRelayPeer())
+ if (!pfrom.IsInboundConn() && pfrom.IsAddrRelayPeer())
{
// Advertise our address
if (fListen && !::ChainstateActive().IsInitialBlockDownload())
{
- CAddress addr = GetLocalAddress(&pfrom->addr, pfrom->GetLocalServices());
+ CAddress addr = GetLocalAddress(&pfrom.addr, pfrom.GetLocalServices());
FastRandomContext insecure_rand;
if (addr.IsRoutable())
{
LogPrint(BCLog::NET, "ProcessMessages: advertising address %s\n", addr.ToString());
- pfrom->PushAddress(addr, insecure_rand);
- } else if (IsPeerAddrLocalGood(pfrom)) {
+ pfrom.PushAddress(addr, insecure_rand);
+ } else if (IsPeerAddrLocalGood(&pfrom)) {
addr.SetIP(addrMe);
LogPrint(BCLog::NET, "ProcessMessages: advertising address %s\n", addr.ToString());
- pfrom->PushAddress(addr, insecure_rand);
+ pfrom.PushAddress(addr, insecure_rand);
}
}
// 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->MarkAddressGood(pfrom->addr);
+ connman.PushMessage(&pfrom, CNetMsgMaker(nSendVersion).Make(NetMsgType::GETADDR));
+ pfrom.fGetAddr = true;
+ connman.MarkAddressGood(pfrom.addr);
}
std::string remoteAddr;
if (fLogIPs)
- remoteAddr = ", peeraddr=" + pfrom->addr.ToString();
+ remoteAddr = ", peeraddr=" + pfrom.addr.ToString();
LogPrint(BCLog::NET, "receive version message: %s: version %d, blocks=%d, us=%s, peer=%d%s\n",
- cleanSubVer, pfrom->nVersion,
- pfrom->nStartingHeight, addrMe.ToString(), pfrom->GetId(),
+ cleanSubVer, pfrom.nVersion,
+ pfrom.nStartingHeight, addrMe.ToString(), pfrom.GetId(),
remoteAddr);
int64_t nTimeOffset = nTime - GetTime();
- pfrom->nTimeOffset = nTimeOffset;
- AddTimeData(pfrom->addr, nTimeOffset);
+ pfrom.nTimeOffset = nTimeOffset;
+ AddTimeData(pfrom.addr, nTimeOffset);
// If the peer is old enough to have the old alert system, send it the final alert.
- if (pfrom->nVersion <= 70012) {
+ if (pfrom.nVersion <= 70012) {
CDataStream finalAlert(ParseHex("60010000000000000000000000ffffff7f00000000ffffff7ffeffff7f01ffffff7f00000000ffffff7f00ffffff7f002f555247454e543a20416c657274206b657920636f6d70726f6d697365642c2075706772616465207265717569726564004630440220653febd6410f470f6bae11cad19c48413becb1ac2c17f908fd0fd53bdc3abd5202206d0e9c96fe88d4a0f01ed9dedae2b6f9e00da94cad0fecaae66ecf689bf71b50"), SER_NETWORK, PROTOCOL_VERSION);
- connman->PushMessage(pfrom, CNetMsgMaker(nSendVersion).Make("alert", finalAlert));
+ 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);
- pfrom->fDisconnect = true;
+ if (pfrom.IsFeelerConn()) {
+ pfrom.fDisconnect = true;
}
- return true;
+ return;
}
- if (pfrom->nVersion == 0) {
+ if (pfrom.nVersion == 0) {
// Must have a version message before anything else
LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 1);
- return false;
+ Misbehaving(pfrom.GetId(), 1, "non-version message before version handshake");
+ return;
}
// At this point, the outgoing message serialization version can't change.
- const CNetMsgMaker msgMaker(pfrom->GetSendVersion());
+ const CNetMsgMaker msgMaker(pfrom.GetSendVersion());
if (msg_type == NetMsgType::VERACK)
{
- pfrom->SetRecvVersion(std::min(pfrom->nVersion.load(), PROTOCOL_VERSION));
+ 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;
+ State(pfrom.GetId())->fCurrentlyConnected = true;
LogPrintf("New outbound peer connected: version: %d, blocks=%d, peer=%d%s (%s)\n",
- pfrom->nVersion.load(), pfrom->nStartingHeight,
- pfrom->GetId(), (fLogIPs ? strprintf(", peeraddr=%s", pfrom->addr.ToString()) : ""),
- pfrom->m_tx_relay == nullptr ? "block-relay" : "full-relay");
+ pfrom.nVersion.load(), pfrom.nStartingHeight,
+ pfrom.GetId(), (fLogIPs ? strprintf(", peeraddr=%s", pfrom.addr.ToString()) : ""),
+ pfrom.m_tx_relay == nullptr ? "block-relay" : "full-relay");
}
- if (pfrom->nVersion >= SENDHEADERS_VERSION) {
+ if (pfrom.nVersion >= SENDHEADERS_VERSION) {
// Tell our peer we prefer to receive headers rather than inv's
// 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));
+ connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDHEADERS));
}
- if (pfrom->nVersion >= SHORT_IDS_BLOCKS_VERSION) {
+ if (pfrom.nVersion >= SHORT_IDS_BLOCKS_VERSION) {
// Tell our peer we are willing to provide version 1 or 2 cmpctblocks
// However, we do not request new block announcements using
// cmpctblock messages.
@@ -2427,37 +2523,53 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
// they may wish to request compact blocks from us
bool fAnnounceUsingCMPCTBLOCK = false;
uint64_t nCMPCTBLOCKVersion = 2;
- if (pfrom->GetLocalServices() & NODE_WITNESS)
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
+ if (pfrom.GetLocalServices() & NODE_WITNESS)
+ connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
nCMPCTBLOCKVersion = 1;
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
+ connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
}
- pfrom->fSuccessfullyConnected = true;
- return true;
+ pfrom.fSuccessfullyConnected = true;
+ return;
+ }
+
+ // Feature negotiation of wtxidrelay should happen between VERSION and
+ // VERACK, to avoid relay problems from switching after a connection is up
+ if (msg_type == NetMsgType::WTXIDRELAY) {
+ if (pfrom.fSuccessfullyConnected) {
+ // Disconnect peers that send wtxidrelay message after VERACK; this
+ // must be negotiated between VERSION and VERACK.
+ pfrom.fDisconnect = true;
+ return;
+ }
+ if (pfrom.nVersion >= WTXID_RELAY_VERSION) {
+ LOCK(cs_main);
+ if (!State(pfrom.GetId())->m_wtxid_relay) {
+ State(pfrom.GetId())->m_wtxid_relay = true;
+ g_wtxid_relay_peers++;
+ }
+ }
+ return;
}
- if (!pfrom->fSuccessfullyConnected) {
+ if (!pfrom.fSuccessfullyConnected) {
// Must have a verack message before anything else
LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 1);
- return false;
+ Misbehaving(pfrom.GetId(), 1, "non-verack message before version handshake");
+ 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;
+ if (!pfrom.IsAddrRelayPeer()) {
+ return;
}
- if (vAddr.size() > 1000)
+ if (vAddr.size() > MAX_ADDR_TO_SEND)
{
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
@@ -2467,7 +2579,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
@@ -2477,53 +2589,56 @@ 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
+ pfrom.AddAddressKnown(addr);
+ 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())
+ 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, connman);
}
// Do not store addresses outside our network
if (fReachable)
vAddrOk.push_back(addr);
}
- connman->AddNewAddresses(vAddrOk, pfrom->addr, 2 * 60 * 60);
+ connman.AddNewAddresses(vAddrOk, pfrom.addr, 2 * 60 * 60);
if (vAddr.size() < 1000)
- pfrom->fGetAddr = false;
- if (pfrom->fOneShot)
- pfrom->fDisconnect = true;
- return true;
+ pfrom.fGetAddr = false;
+ if (pfrom.IsAddrFetchConn())
+ pfrom.fDisconnect = true;
+ return;
}
if (msg_type == NetMsgType::SENDHEADERS) {
LOCK(cs_main);
- State(pfrom->GetId())->fPreferHeaders = true;
- return true;
+ State(pfrom.GetId())->fPreferHeaders = true;
+ return;
}
if (msg_type == NetMsgType::SENDCMPCT) {
bool fAnnounceUsingCMPCTBLOCK = false;
uint64_t nCMPCTBLOCKVersion = 0;
vRecv >> fAnnounceUsingCMPCTBLOCK >> nCMPCTBLOCKVersion;
- if (nCMPCTBLOCKVersion == 1 || ((pfrom->GetLocalServices() & NODE_WITNESS) && nCMPCTBLOCKVersion == 2)) {
+ if (nCMPCTBLOCKVersion == 1 || ((pfrom.GetLocalServices() & NODE_WITNESS) && nCMPCTBLOCKVersion == 2)) {
LOCK(cs_main);
// fProvidesHeaderAndIDs is used to "lock in" version of compact blocks we send (fWantsCmpctWitness)
- if (!State(pfrom->GetId())->fProvidesHeaderAndIDs) {
- State(pfrom->GetId())->fProvidesHeaderAndIDs = true;
- State(pfrom->GetId())->fWantsCmpctWitness = nCMPCTBLOCKVersion == 2;
+ if (!State(pfrom.GetId())->fProvidesHeaderAndIDs) {
+ State(pfrom.GetId())->fProvidesHeaderAndIDs = true;
+ State(pfrom.GetId())->fWantsCmpctWitness = nCMPCTBLOCKVersion == 2;
}
- if (State(pfrom->GetId())->fWantsCmpctWitness == (nCMPCTBLOCKVersion == 2)) // ignore later version announces
- State(pfrom->GetId())->fPreferHeaderAndIDs = fAnnounceUsingCMPCTBLOCK;
- if (!State(pfrom->GetId())->fSupportsDesiredCmpctVersion) {
- if (pfrom->GetLocalServices() & NODE_WITNESS)
- State(pfrom->GetId())->fSupportsDesiredCmpctVersion = (nCMPCTBLOCKVersion == 2);
+ if (State(pfrom.GetId())->fWantsCmpctWitness == (nCMPCTBLOCKVersion == 2)) // ignore later version announces
+ State(pfrom.GetId())->fPreferHeaderAndIDs = fAnnounceUsingCMPCTBLOCK;
+ if (!State(pfrom.GetId())->fSupportsDesiredCmpctVersion) {
+ if (pfrom.GetLocalServices() & NODE_WITNESS)
+ State(pfrom.GetId())->fSupportsDesiredCmpctVersion = (nCMPCTBLOCKVersion == 2);
else
- State(pfrom->GetId())->fSupportsDesiredCmpctVersion = (nCMPCTBLOCKVersion == 1);
+ State(pfrom.GetId())->fSupportsDesiredCmpctVersion = (nCMPCTBLOCKVersion == 1);
}
}
- return true;
+ return;
}
if (msg_type == NetMsgType::INV) {
@@ -2532,17 +2647,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);
+ 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);
@@ -2553,17 +2669,26 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
for (CInv &inv : vInv)
{
if (interruptMsgProc)
- return true;
+ return;
+
+ // 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.IsMsgTx()) continue;
+ } else {
+ if (inv.IsMsgWtx()) continue;
+ }
bool fAlreadyHave = AlreadyHave(inv, mempool);
- LogPrint(BCLog::NET, "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom->GetId());
+ 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;
}
if (inv.type == MSG_BLOCK) {
- UpdateBlockAvailability(pfrom->GetId(), inv.hash);
+ UpdateBlockAvailability(pfrom.GetId(), inv.hash);
if (!fAlreadyHave && !fImporting && !fReindex && !mapBlocksInFlight.count(inv.hash)) {
// Headers-first is the primary method of announcement on
// the network. If a node fell back to sending blocks by inv,
@@ -2573,23 +2698,23 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
best_block = &inv.hash;
}
} else {
- pfrom->AddInventoryKnown(inv);
+ pfrom.AddKnownTx(inv.hash);
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()) {
- RequestTx(State(pfrom->GetId()), inv.hash, current_time);
+ 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()), ToGenTxid(inv), current_time);
}
}
}
if (best_block != nullptr) {
- 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());
+ 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());
}
- return true;
+ return;
}
if (msg_type == NetMsgType::GETDATA) {
@@ -2598,19 +2723,19 @@ 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());
+ LogPrint(BCLog::NET, "received getdata (%u invsz) peer=%d\n", vInv.size(), pfrom.GetId());
if (vInv.size() > 0) {
- LogPrint(BCLog::NET, "received getdata for: %s peer=%d\n", vInv[0].ToString(), pfrom->GetId());
+ LogPrint(BCLog::NET, "received getdata for: %s peer=%d\n", vInv[0].ToString(), pfrom.GetId());
}
- pfrom->vRecvGetData.insert(pfrom->vRecvGetData.end(), vInv.begin(), vInv.end());
+ pfrom.vRecvGetData.insert(pfrom.vRecvGetData.end(), vInv.begin(), vInv.end());
ProcessGetData(pfrom, chainparams, connman, mempool, interruptMsgProc);
- return true;
+ return;
}
if (msg_type == NetMsgType::GETBLOCKS) {
@@ -2619,9 +2744,9 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
vRecv >> locator >> hashStop;
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;
+ LogPrint(BCLog::NET, "getblocks locator size %lld > %d, disconnect peer=%d\n", locator.vHave.size(), MAX_LOCATOR_SZ, pfrom.GetId());
+ pfrom.fDisconnect = true;
+ return;
}
// We might have announced the currently-being-connected tip using a
@@ -2652,7 +2777,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
if (pindex)
pindex = ::ChainActive().Next(pindex);
int nLimit = 500;
- LogPrint(BCLog::NET, "getblocks %d to %s limit %d from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), nLimit, pfrom->GetId());
+ LogPrint(BCLog::NET, "getblocks %d to %s limit %d from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), nLimit, pfrom.GetId());
for (; pindex; pindex = ::ChainActive().Next(pindex))
{
if (pindex->GetBlockHash() == hashStop)
@@ -2668,17 +2793,17 @@ 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
// trigger the peer to getblocks the next batch of inventory.
LogPrint(BCLog::NET, " getblocks stopping at limit %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
- pfrom->hashContinue = pindex->GetBlockHash();
+ pfrom.hashContinue = pindex->GetBlockHash();
break;
}
}
- return true;
+ return;
}
if (msg_type == NetMsgType::GETBLOCKTXN) {
@@ -2694,15 +2819,15 @@ 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);
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;
+ LogPrint(BCLog::NET, "Peer %d sent us a getblocktxn for a block we don't have\n", pfrom.GetId());
+ return;
}
if (pindex->nHeight < ::ChainActive().Height() - MAX_BLOCKTXN_DEPTH) {
@@ -2713,13 +2838,13 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
// might maliciously send lots of getblocktxn requests to trigger
// expensive disk reads, because it will require the peer to
// actually receive all the data read from disk over the network.
- LogPrint(BCLog::NET, "Peer %d sent us a getblocktxn for a block > %i deep\n", pfrom->GetId(), MAX_BLOCKTXN_DEPTH);
+ LogPrint(BCLog::NET, "Peer %d sent us a getblocktxn for a block > %i deep\n", pfrom.GetId(), MAX_BLOCKTXN_DEPTH);
CInv inv;
- inv.type = State(pfrom->GetId())->fWantsCmpctWitness ? MSG_WITNESS_BLOCK : MSG_BLOCK;
+ inv.type = State(pfrom.GetId())->fWantsCmpctWitness ? MSG_WITNESS_BLOCK : MSG_BLOCK;
inv.hash = req.blockhash;
- pfrom->vRecvGetData.push_back(inv);
+ 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;
@@ -2727,7 +2852,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) {
@@ -2736,30 +2861,30 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
vRecv >> locator >> hashStop;
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;
+ LogPrint(BCLog::NET, "getheaders locator size %lld > %d, disconnect peer=%d\n", locator.vHave.size(), MAX_LOCATOR_SZ, pfrom.GetId());
+ pfrom.fDisconnect = true;
+ return;
}
LOCK(cs_main);
- if (::ChainstateActive().IsInitialBlockDownload() && !pfrom->HasPermission(PF_NOBAN)) {
- LogPrint(BCLog::NET, "Ignoring getheaders from peer=%d because node is in initial block download\n", pfrom->GetId());
- return true;
+ 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;
}
- CNodeState *nodestate = State(pfrom->GetId());
+ CNodeState *nodestate = State(pfrom.GetId());
const CBlockIndex* pindex = nullptr;
if (locator.IsNull())
{
// 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;
+ 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;
}
}
else
@@ -2773,7 +2898,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
// we must use CBlocks, as CBlockHeaders won't include the 0x00 nTx count at the end
std::vector<CBlock> vHeaders;
int nLimit = MAX_HEADERS_RESULTS;
- LogPrint(BCLog::NET, "getheaders %d to %s from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), pfrom->GetId());
+ LogPrint(BCLog::NET, "getheaders %d to %s from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), pfrom.GetId());
for (; pindex; pindex = ::ChainActive().Next(pindex))
{
vHeaders.push_back(pindex->GetBlockHeader());
@@ -2793,67 +2918,104 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
// 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));
- return true;
+ connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::HEADERS, vHeaders));
+ return;
}
if (msg_type == NetMsgType::TX) {
// Stop processing the transaction early if
// 1) We are in blocks only mode and peer has no relay permission
// 2) This peer is a block-relay-only peer
- if ((!g_relay_txes && !pfrom->HasPermission(PF_RELAY)) || (pfrom->m_tx_relay == nullptr))
+ if ((!g_relay_txes && !pfrom.HasPermission(PF_RELAY)) || (pfrom.m_tx_relay == nullptr))
{
- LogPrint(BCLog::NET, "transaction sent in violation of protocol peer=%d\n", pfrom->GetId());
- pfrom->fDisconnect = true;
- return true;
+ LogPrint(BCLog::NET, "transaction sent in violation of protocol peer=%d\n", pfrom.GetId());
+ pfrom.fDisconnect = true;
+ return;
}
CTransactionRef ptx;
vRecv >> ptx;
const CTransaction& tx = *ptx;
- CInv inv(MSG_TX, tx.GetHash());
- pfrom->AddInventoryKnown(inv);
+ const uint256& txid = ptx->GetHash();
+ const uint256& wtxid = ptx->GetWitnessHash();
LOCK2(cs_main, g_cs_orphans);
+ CNodeState* nodestate = State(pfrom.GetId());
+
+ const uint256& hash = nodestate->m_wtxid_relay ? wtxid : txid;
+ pfrom.AddKnownTx(hash);
+ if (nodestate->m_wtxid_relay && txid != wtxid) {
+ // Insert txid into filterInventoryKnown, even for
+ // wtxidrelay peers. This prevents re-adding of
+ // unconfirmed parents to the recently_announced
+ // filter, when a child tx is requested. See
+ // ProcessGetData().
+ pfrom.AddKnownTx(txid);
+ }
+
TxValidationState state;
- CNodeState* nodestate = State(pfrom->GetId());
- nodestate->m_tx_download.m_tx_announced.erase(inv.hash);
- nodestate->m_tx_download.m_tx_in_flight.erase(inv.hash);
- EraseTxRequest(inv.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;
- if (!AlreadyHave(inv, mempool) &&
+ // 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 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
+ // was confirmed with a different witness, or exists in our mempool
+ // with a different witness, but this has limited downside:
+ // mempool validation does its own lookup of whether we have the txid
+ // 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(), *connman);
+ RelayTransaction(tx.GetHash(), tx.GetWitnessHash(), connman);
for (unsigned int i = 0; i < tx.vout.size(); i++) {
- auto it_by_prev = mapOrphanTransactionsByPrev.find(COutPoint(inv.hash, i));
+ auto it_by_prev = mapOrphanTransactionsByPrev.find(COutPoint(txid, i));
if (it_by_prev != mapOrphanTransactionsByPrev.end()) {
for (const auto& elem : it_by_prev->second) {
- pfrom->orphan_work_set.insert(elem->first);
+ pfrom.orphan_work_set.insert(elem->first);
}
}
}
- pfrom->nLastTXTime = GetTime();
+ pfrom.nLastTXTime = GetTime();
LogPrint(BCLog::MEMPOOL, "AcceptToMemoryPool: peer=%d: accepted %s (poolsz %u txn, %u kB)\n",
- pfrom->GetId(),
+ pfrom.GetId(),
tx.GetHash().ToString(),
mempool.size(), mempool.DynamicMemoryUsage() / 1000);
// Recursively process any orphan transactions that depended on this one
- ProcessOrphanTx(connman, mempool, pfrom->orphan_work_set, lRemovedTxn);
+ ProcessOrphanTx(connman, 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;
}
@@ -2862,12 +3024,17 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
uint32_t nFetchFlags = GetFetchFlags(pfrom);
const auto current_time = GetTime<std::chrono::microseconds>();
- for (const CTxIn& txin : tx.vin) {
- CInv _inv(MSG_TX | nFetchFlags, txin.prevout.hash);
- pfrom->AddInventoryKnown(_inv);
- 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, mempool)) RequestTx(State(pfrom.GetId()), ToGenTxid(_inv), current_time);
}
- AddOrphanTx(ptx, pfrom->GetId());
+ AddOrphanTx(ptx, pfrom.GetId());
// DoS prevention: do not allow mapOrphanTransactions to grow unbounded (see CVE-2012-3789)
unsigned int nMaxOrphanTx = (unsigned int)std::max((int64_t)0, gArgs.GetArg("-maxorphantx", DEFAULT_MAX_ORPHAN_TRANSACTIONS));
@@ -2879,15 +3046,41 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
LogPrint(BCLog::MEMPOOL, "not keeping orphan with rejected parents %s\n",tx.GetHash().ToString());
// We will continue to reject this tx since it has rejected
// parents so avoid re-requesting it from other peers.
+ // Here we add both the txid and the wtxid, as we know that
+ // regardless of what witness is provided, we will not accept
+ // this, so we don't need to allow for redownload of this txid
+ // from any of our non-wtxidrelay peers.
recentRejects->insert(tx.GetHash());
+ recentRejects->insert(tx.GetWitnessHash());
}
} else {
- if (!tx.HasWitness() && state.GetResult() != TxValidationResult::TX_WITNESS_MUTATED) {
- // Do not use rejection cache for witness transactions or
- // witness-stripped transactions, as they can have been malleated.
- // See https://github.com/bitcoin/bitcoin/issues/8279 for details.
+ if (state.GetResult() != TxValidationResult::TX_WITNESS_STRIPPED) {
+ // We can add the wtxid of this transaction to our reject filter.
+ // Do not add txids of witness transactions or witness-stripped
+ // transactions to the filter, as they can have been malleated;
+ // adding such txids to the reject filter would potentially
+ // interfere with relay of valid transactions from peers that
+ // do not support wtxid-based relay. See
+ // https://github.com/bitcoin/bitcoin/issues/8279 for details.
+ // We can remove this restriction (and always add wtxids to
+ // the filter even for witness stripped transactions) once
+ // wtxid-based relay is broadly deployed.
+ // See also comments in https://github.com/bitcoin/bitcoin/pull/18044#discussion_r443419034
+ // for concerns around weakening security of unupgraded nodes
+ // if we start doing this too early.
assert(recentRejects);
- recentRejects->insert(tx.GetHash());
+ 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);
}
@@ -2895,16 +3088,16 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
AddToCompactExtraTransactions(ptx);
}
- if (pfrom->HasPermission(PF_FORCERELAY)) {
- // Always relay transactions received from whitelisted peers, even
+ if (pfrom.HasPermission(PF_FORCERELAY)) {
+ // 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());
- RelayTransaction(tx.GetHash(), *connman);
+ LogPrintf("Force relaying tx %s from peer=%d\n", tx.GetHash().ToString(), pfrom.GetId());
+ RelayTransaction(tx.GetHash(), tx.GetWitnessHash(), connman);
}
}
}
@@ -2929,22 +3122,21 @@ 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(),
+ pfrom.GetId(),
state.ToString());
- MaybePunishNodeForTx(pfrom->GetId(), state);
+ MaybePunishNodeForTx(pfrom.GetId(), state);
}
- return true;
+ return;
}
if (msg_type == NetMsgType::CMPCTBLOCK)
{
// Ignore cmpctblock received while importing
if (fImporting || fReindex) {
- LogPrint(BCLog::NET, "Unexpected cmpctblock message received from peer %d\n", pfrom->GetId());
- return true;
+ LogPrint(BCLog::NET, "Unexpected cmpctblock message received from peer %d\n", pfrom.GetId());
+ return;
}
CBlockHeaderAndShortTxIDs cmpctblock;
@@ -2958,8 +3150,8 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
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()));
- return true;
+ connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), uint256()));
+ return;
}
if (!LookupBlockIndex(cmpctblock.header.GetHash())) {
@@ -2971,8 +3163,8 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
BlockValidationState state;
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;
+ MaybePunishNodeForBlock(pfrom.GetId(), state, /*via_compact_block*/ true, "invalid header via cmpctblock");
+ return;
}
}
@@ -2996,9 +3188,9 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
LOCK2(cs_main, g_cs_orphans);
// If AcceptBlockHeader returned true, it set pindex
assert(pindex);
- UpdateBlockAvailability(pfrom->GetId(), pindex->GetBlockHash());
+ UpdateBlockAvailability(pfrom.GetId(), pindex->GetBlockHash());
- CNodeState *nodestate = State(pfrom->GetId());
+ CNodeState *nodestate = State(pfrom.GetId());
// If this was a new header with more work than our tip, update the
// peer's last block announcement time
@@ -3010,7 +3202,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
@@ -3019,49 +3211,49 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
// 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));
+ 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
// possibilities in compact block processing...
if (pindex->nHeight <= ::ChainActive().Height() + 2) {
if ((!fAlreadyInFlight && nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) ||
- (fAlreadyInFlight && blockInFlightIt->second.first == pfrom->GetId())) {
+ (fAlreadyInFlight && blockInFlightIt->second.first == pfrom.GetId())) {
std::list<QueuedBlock>::iterator* queuedBlockIt = nullptr;
- if (!MarkBlockAsInFlight(mempool, pfrom->GetId(), pindex->GetBlockHash(), pindex, &queuedBlockIt)) {
+ if (!MarkBlockAsInFlight(mempool, pfrom.GetId(), pindex->GetBlockHash(), pindex, &queuedBlockIt)) {
if (!(*queuedBlockIt)->partialBlock)
(*queuedBlockIt)->partialBlock.reset(new PartiallyDownloadedBlock(&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");
- 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
- Misbehaving(pfrom->GetId(), 100, strprintf("Peer %d sent us invalid compact block\n", pfrom->GetId()));
- return true;
+ MarkBlockAsReceived(pindex->GetBlockHash()); // Reset in-flight state in case Misbehaving does not result in a disconnect
+ 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));
- return true;
+ connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv));
+ return;
}
BlockTransactionsRequest req;
@@ -3077,7 +3269,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
fProcessBLOCKTXN = true;
} else {
req.blockhash = pindex->GetBlockHash();
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETBLOCKTXN, req));
+ connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETBLOCKTXN, req));
}
} else {
// This block is either already in flight from a different
@@ -3089,7 +3281,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);
@@ -3103,8 +3295,8 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
// 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));
- return true;
+ 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
fRevertToHeaderProcessing = true;
@@ -3113,14 +3305,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);
}
@@ -3129,7 +3321,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
// block that is in flight from some other peer.
{
LOCK(cs_main);
- mapBlockSource.emplace(pblock->GetHash(), std::make_pair(pfrom->GetId(), false));
+ mapBlockSource.emplace(pblock->GetHash(), std::make_pair(pfrom.GetId(), false));
}
bool fNewBlock = false;
// Setting fForceProcessing to true means that we bypass some of
@@ -3143,7 +3335,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
// reconstructed compact blocks as having been requested.
chainman.ProcessNewBlock(chainparams, pblock, /*fForceProcessing=*/true, &fNewBlock);
if (fNewBlock) {
- pfrom->nLastBlockTime = GetTime();
+ pfrom.nLastBlockTime = GetTime();
} else {
LOCK(cs_main);
mapBlockSource.erase(pblock->GetHash());
@@ -3157,15 +3349,15 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
MarkBlockAsReceived(pblock->GetHash());
}
}
- return true;
+ return;
}
if (msg_type == NetMsgType::BLOCKTXN)
{
// Ignore blocktxn received while importing
if (fImporting || fReindex) {
- LogPrint(BCLog::NET, "Unexpected blocktxn message received from peer %d\n", pfrom->GetId());
- return true;
+ LogPrint(BCLog::NET, "Unexpected blocktxn message received from peer %d\n", pfrom.GetId());
+ return;
}
BlockTransactions resp;
@@ -3178,22 +3370,22 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
std::map<uint256, std::pair<NodeId, std::list<QueuedBlock>::iterator> >::iterator it = mapBlocksInFlight.find(resp.blockhash);
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;
+ it->second.first != pfrom.GetId()) {
+ LogPrint(BCLog::NET, "Peer %d sent us block transactions for block we weren't expecting\n", pfrom.GetId());
+ 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
- Misbehaving(pfrom->GetId(), 100, strprintf("Peer %d sent us invalid compact block/non-matching block transactions\n", pfrom->GetId()));
- return true;
+ MarkBlockAsReceived(resp.blockhash); // Reset in-flight state in case Misbehaving does not result in a disconnect
+ 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));
+ connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, invs));
} else {
// Block is either okay, or possibly we received
// READ_STATUS_CHECKBLOCK_FAILED.
@@ -3206,7 +3398,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
@@ -3220,7 +3412,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
// BIP 152 permits peers to relay compact blocks after validating
// the header only; we should not punish peers if the block turns
// out to be invalid.
- mapBlockSource.emplace(resp.blockhash, std::make_pair(pfrom->GetId(), false));
+ mapBlockSource.emplace(resp.blockhash, std::make_pair(pfrom.GetId(), false));
}
} // Don't hold cs_main when we call into ProcessNewBlock
if (fBlockRead) {
@@ -3233,21 +3425,21 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
// in compact block optimistic reconstruction handling.
chainman.ProcessNewBlock(chainparams, pblock, /*fForceProcessing=*/true, &fNewBlock);
if (fNewBlock) {
- pfrom->nLastBlockTime = GetTime();
+ pfrom.nLastBlockTime = GetTime();
} else {
LOCK(cs_main);
mapBlockSource.erase(pblock->GetHash());
}
}
- return true;
+ return;
}
if (msg_type == NetMsgType::HEADERS)
{
// Ignore headers received while importing
if (fImporting || fReindex) {
- LogPrint(BCLog::NET, "Unexpected headers message received from peer %d\n", pfrom->GetId());
- return true;
+ LogPrint(BCLog::NET, "Unexpected headers message received from peer %d\n", pfrom.GetId());
+ return;
}
std::vector<CBlockHeader> headers;
@@ -3256,8 +3448,8 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
unsigned int nCount = ReadCompactSize(vRecv);
if (nCount > MAX_HEADERS_RESULTS) {
LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 20, strprintf("headers message size = %u", nCount));
- return false;
+ Misbehaving(pfrom.GetId(), 20, strprintf("headers message size = %u", nCount));
+ return;
}
headers.resize(nCount);
for (unsigned int n = 0; n < nCount; n++) {
@@ -3272,14 +3464,14 @@ 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;
+ LogPrint(BCLog::NET, "Unexpected block message received from peer %d\n", pfrom.GetId());
+ return;
}
std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>();
vRecv >> *pblock;
- LogPrint(BCLog::NET, "received block %s peer=%d\n", pblock->GetHash().ToString(), pfrom->GetId());
+ LogPrint(BCLog::NET, "received block %s peer=%d\n", pblock->GetHash().ToString(), pfrom.GetId());
bool forceProcessing = false;
const uint256 hash(pblock->GetHash());
@@ -3291,17 +3483,17 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
// mapBlockSource is only used for punishing peers and setting
// which peers send us compact blocks, so the race between here and
// cs_main in ProcessNewBlock is fine.
- mapBlockSource.emplace(hash, std::make_pair(pfrom->GetId(), true));
+ mapBlockSource.emplace(hash, std::make_pair(pfrom.GetId(), true));
}
bool fNewBlock = false;
chainman.ProcessNewBlock(chainparams, pblock, forceProcessing, &fNewBlock);
if (fNewBlock) {
- pfrom->nLastBlockTime = GetTime();
+ pfrom.nLastBlockTime = GetTime();
} else {
LOCK(cs_main);
mapBlockSource.erase(pblock->GetHash());
}
- return true;
+ return;
}
if (msg_type == NetMsgType::GETADDR) {
@@ -3310,64 +3502,67 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
// 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) {
- LogPrint(BCLog::NET, "Ignoring \"getaddr\" from outbound connection. peer=%d\n", pfrom->GetId());
- return true;
+ if (!pfrom.IsInboundConn()) {
+ LogPrint(BCLog::NET, "Ignoring \"getaddr\" from outbound connection. peer=%d\n", pfrom.GetId());
+ return;
}
- if (!pfrom->IsAddrRelayPeer()) {
- LogPrint(BCLog::NET, "Ignoring \"getaddr\" from block-relay-only connection. peer=%d\n", pfrom->GetId());
- return true;
+ if (!pfrom.IsAddrRelayPeer()) {
+ LogPrint(BCLog::NET, "Ignoring \"getaddr\" from block-relay-only connection. peer=%d\n", pfrom.GetId());
+ 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;
+ if (pfrom.fSentAddr) {
+ LogPrint(BCLog::NET, "Ignoring repeated \"getaddr\". peer=%d\n", pfrom.GetId());
+ return;
}
- pfrom->fSentAddr = true;
+ pfrom.fSentAddr = true;
- pfrom->vAddrToSend.clear();
- std::vector<CAddress> vAddr = connman->GetAddresses();
+ pfrom.vAddrToSend.clear();
+ std::vector<CAddress> vAddr;
+ if (pfrom.HasPermission(PF_ADDR)) {
+ vAddr = connman.GetAddresses(MAX_ADDR_TO_SEND, MAX_PCT_ADDR_TO_SEND);
+ } else {
+ vAddr = connman.GetAddresses(pfrom.addr.GetNetwork(), MAX_ADDR_TO_SEND, MAX_PCT_ADDR_TO_SEND);
+ }
FastRandomContext insecure_rand;
for (const CAddress &addr : vAddr) {
- if (!banman->IsBanned(addr)) {
- pfrom->PushAddress(addr, insecure_rand);
- }
+ pfrom.PushAddress(addr, insecure_rand);
}
- return true;
+ return;
}
if (msg_type == NetMsgType::MEMPOOL) {
- if (!(pfrom->GetLocalServices() & NODE_BLOOM) && !pfrom->HasPermission(PF_MEMPOOL))
+ if (!(pfrom.GetLocalServices() & NODE_BLOOM) && !pfrom.HasPermission(PF_MEMPOOL))
{
- if (!pfrom->HasPermission(PF_NOBAN))
+ if (!pfrom.HasPermission(PF_NOBAN))
{
- LogPrint(BCLog::NET, "mempool request with bloom filters disabled, disconnect peer=%d\n", pfrom->GetId());
- pfrom->fDisconnect = true;
+ 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))
+ if (connman.OutboundTargetReached(false) && !pfrom.HasPermission(PF_MEMPOOL))
{
- if (!pfrom->HasPermission(PF_NOBAN))
+ if (!pfrom.HasPermission(PF_NOBAN))
{
- LogPrint(BCLog::NET, "mempool request with bandwidth limit reached, disconnect peer=%d\n", pfrom->GetId());
- pfrom->fDisconnect = true;
+ 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;
+ 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) {
- if (pfrom->nVersion > BIP0031_VERSION)
+ if (pfrom.nVersion > BIP0031_VERSION)
{
uint64_t nonce = 0;
vRecv >> nonce;
@@ -3382,13 +3577,13 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
// 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));
+ 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;
@@ -3398,15 +3593,15 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
vRecv >> nonce;
// Only process pong message if there is an outstanding ping (old ping without nonce should never pong)
- if (pfrom->nPingNonceSent != 0) {
- if (nonce == pfrom->nPingNonceSent) {
+ if (pfrom.nPingNonceSent != 0) {
+ 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";
@@ -3431,19 +3626,23 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
if (!(sProblem.empty())) {
LogPrint(BCLog::NET, "pong peer=%d: %s, %x expected, %x received, %u bytes\n",
- pfrom->GetId(),
+ pfrom.GetId(),
sProblem,
- pfrom->nPingNonceSent,
+ pfrom.nPingNonceSent,
nonce,
nAvail);
}
if (bPingFinished) {
- pfrom->nPingNonceSent = 0;
+ pfrom.nPingNonceSent = 0;
}
- return true;
+ return;
}
if (msg_type == NetMsgType::FILTERLOAD) {
+ if (!(pfrom.GetLocalServices() & NODE_BLOOM)) {
+ pfrom.fDisconnect = true;
+ return;
+ }
CBloomFilter filter;
vRecv >> filter;
@@ -3451,18 +3650,22 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
{
// 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)
+ else if (pfrom.m_tx_relay != nullptr)
{
- LOCK(pfrom->m_tx_relay->cs_filter);
- pfrom->m_tx_relay->pfilter.reset(new CBloomFilter(filter));
- pfrom->m_tx_relay->fRelayTxes = true;
+ LOCK(pfrom.m_tx_relay->cs_filter);
+ 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;
@@ -3471,70 +3674,72 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
bool bad = false;
if (vData.size() > MAX_SCRIPT_ELEMENT_SIZE) {
bad = true;
- } else if (pfrom->m_tx_relay != nullptr) {
- LOCK(pfrom->m_tx_relay->cs_filter);
- if (pfrom->m_tx_relay->pfilter) {
- pfrom->m_tx_relay->pfilter->insert(vData);
+ } else if (pfrom.m_tx_relay != nullptr) {
+ LOCK(pfrom.m_tx_relay->cs_filter);
+ if (pfrom.m_tx_relay->pfilter) {
+ pfrom.m_tx_relay->pfilter->insert(vData);
} else {
bad = true;
}
}
if (bad) {
LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 100);
+ Misbehaving(pfrom.GetId(), 100, "bad filteradd message");
}
- return true;
+ return;
}
if (msg_type == NetMsgType::FILTERCLEAR) {
- if (pfrom->m_tx_relay == nullptr) {
- return true;
+ if (!(pfrom.GetLocalServices() & NODE_BLOOM)) {
+ pfrom.fDisconnect = true;
+ return;
}
- LOCK(pfrom->m_tx_relay->cs_filter);
- if (pfrom->GetLocalServices() & NODE_BLOOM) {
- pfrom->m_tx_relay->pfilter = nullptr;
+ if (pfrom.m_tx_relay == nullptr) {
+ return;
}
- pfrom->m_tx_relay->fRelayTxes = true;
- return true;
+ LOCK(pfrom.m_tx_relay->cs_filter);
+ pfrom.m_tx_relay->pfilter = nullptr;
+ pfrom.m_tx_relay->fRelayTxes = true;
+ return;
}
if (msg_type == NetMsgType::FEEFILTER) {
CAmount newFeeFilter = 0;
vRecv >> newFeeFilter;
if (MoneyRange(newFeeFilter)) {
- if (pfrom->m_tx_relay != nullptr) {
- LOCK(pfrom->m_tx_relay->cs_feeFilter);
- pfrom->m_tx_relay->minFeeFilter = newFeeFilter;
+ if (pfrom.m_tx_relay != nullptr) {
+ LOCK(pfrom.m_tx_relay->cs_feeFilter);
+ pfrom.m_tx_relay->minFeeFilter = newFeeFilter;
}
- LogPrint(BCLog::NET, "received: feefilter of %s from peer=%d\n", CFeeRate(newFeeFilter).ToString(), pfrom->GetId());
+ 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;
+ ProcessGetCFilters(pfrom, vRecv, chainparams, connman);
+ return;
}
if (msg_type == NetMsgType::GETCFHEADERS) {
- ProcessGetCFHeaders(*pfrom, vRecv, chainparams, *connman);
- return true;
+ ProcessGetCFHeaders(pfrom, vRecv, chainparams, connman);
+ return;
}
if (msg_type == NetMsgType::GETCFCHECKPT) {
- ProcessGetCFCheckPt(*pfrom, vRecv, chainparams, *connman);
- return true;
+ ProcessGetCFCheckPt(pfrom, vRecv, chainparams, connman);
+ return;
}
if (msg_type == NetMsgType::NOTFOUND) {
// Remove the NOTFOUND transactions from the peer
LOCK(cs_main);
- CNodeState *state = State(pfrom->GetId());
+ CNodeState *state = State(pfrom.GetId());
std::vector<CInv> vInv;
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) {
+ 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);
@@ -3548,39 +3753,57 @@ 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;
+ LogPrint(BCLog::NET, "Unknown command \"%s\" from peer=%d\n", SanitizeString(msg_type), pfrom.GetId());
+ return;
}
-bool PeerLogicValidation::CheckIfBanned(CNode* pnode)
+/** 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());
-
- if (state.fShouldBan) {
- state.fShouldBan = 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 ban _this_ local node
- LogPrintf("Warning: disconnecting but not banning local peer %s!\n", pnode->addr.ToString());
- pnode->fDisconnect = true;
- } else {
- // Disconnect and ban all nodes sharing the address
- if (m_banman) {
- m_banman->Ban(pnode->addr, BanReasonNodeMisbehaving);
- }
- connman->DisconnectNode(pnode->addr);
- }
+ 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;
+
+ state.m_should_discourage = false;
+ } // 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);
+ connman->DisconnectNode(pnode.addr);
+ return true;
}
bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& interruptMsgProc)
@@ -3597,12 +3820,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, *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(*connman, m_mempool, pfrom->orphan_work_set, removed_txn);
for (const CTransactionRef& removedTx : removed_txn) {
AddToCompactExtraTransactions(removedTx);
}
@@ -3661,11 +3884,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())
@@ -3676,24 +3896,17 @@ 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);
-
return fMoreWork;
}
-void PeerLogicValidation::ConsiderEviction(CNode *pto, int64_t time_in_seconds)
+void PeerLogicValidation::ConsiderEviction(CNode& pto, int64_t time_in_seconds)
{
AssertLockHeld(cs_main);
- CNodeState &state = *State(pto->GetId());
- const CNetMsgMaker msgMaker(pto->GetSendVersion());
+ 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
@@ -3720,12 +3933,12 @@ void PeerLogicValidation::ConsiderEviction(CNode *pto, int64_t time_in_seconds)
// message to give the peer a chance to update us.
if (state.m_chain_sync.m_sent_getheaders) {
// They've run out of time to catch up!
- LogPrintf("Disconnecting outbound peer %d for old chain, best known block = %s\n", pto->GetId(), state.pindexBestKnownBlock != nullptr ? state.pindexBestKnownBlock->GetBlockHash().ToString() : "<none>");
- pto->fDisconnect = true;
+ LogPrintf("Disconnecting outbound peer %d for old chain, best known block = %s\n", pto.GetId(), state.pindexBestKnownBlock != nullptr ? state.pindexBestKnownBlock->GetBlockHash().ToString() : "<none>");
+ pto.fDisconnect = true;
} 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()));
+ 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()));
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
@@ -3755,7 +3968,7 @@ void PeerLogicValidation::EvictExtraOutboundPeers(int64_t time_in_seconds)
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
@@ -3825,17 +4038,19 @@ namespace {
class CompareInvMempoolOrder
{
CTxMemPool *mp;
+ bool m_wtxid_relay;
public:
- explicit CompareInvMempoolOrder(CTxMemPool *_mempool)
+ explicit CompareInvMempoolOrder(CTxMemPool *_mempool, bool use_wtxid)
{
mp = _mempool;
+ m_wtxid_relay = use_wtxid;
}
bool operator()(std::set<uint256>::iterator a, std::set<uint256>::iterator b)
{
/* As std::make_heap produces a max-heap, we want the entries with the
* fewest ancestors/highest fee to sort later. */
- return mp->CompareDepthAndScore(*b, *a);
+ return mp->CompareDepthAndScore(*b, *a, m_wtxid_relay);
}
};
}
@@ -3843,48 +4058,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->nPingUsecStart + PING_INTERVAL * 1000000 < GetTimeMicros()) {
- // 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->nPingUsecStart = GetTimeMicros();
- 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 (CheckIfBanned(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));
+ }
+ }
+
+ {
+ LOCK(cs_main);
CNodeState &state = *State(pto->GetId());
@@ -3911,8 +4127,8 @@ 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));
vAddr.clear();
@@ -3930,7 +4146,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) {
@@ -4082,7 +4298,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());
}
@@ -4115,7 +4331,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
bool fSendTrickle = pto->HasPermission(PF_NOBAN);
if (pto->m_tx_relay->nNextInvSend < current_time) {
fSendTrickle = true;
- if (pto->fInbound) {
+ if (pto->IsInboundConn()) {
pto->m_tx_relay->nNextInvSend = std::chrono::microseconds{connman->PoissonNextSendInbound(nNow, INVENTORY_BROADCAST_INTERVAL)};
} else {
// Use half the delay for outbound peers, as there is less privacy concern for them.
@@ -4142,8 +4358,8 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
LOCK(pto->m_tx_relay->cs_filter);
for (const auto& txinfo : vtxinfo) {
- const uint256& hash = txinfo.tx->GetHash();
- CInv inv(MSG_TX, hash);
+ const uint256& hash = state.m_wtxid_relay ? txinfo.tx->GetWitnessHash() : txinfo.tx->GetHash();
+ CInv inv(state.m_wtxid_relay ? MSG_WTX : MSG_TX, hash);
pto->m_tx_relay->setInventoryTxToSend.erase(hash);
// Don't send transactions that peers will not put into their mempool
if (txinfo.fee < filterrate.GetFee(txinfo.vsize)) {
@@ -4153,6 +4369,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));
@@ -4177,7 +4394,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
}
// Topologically and fee-rate sort the inventory we send for privacy and priority reasons.
// A heap is used so that not all items need sorting if only a few are being sent.
- CompareInvMempoolOrder compareInvMempoolOrder(&m_mempool);
+ CompareInvMempoolOrder compareInvMempoolOrder(&m_mempool, state.m_wtxid_relay);
std::make_heap(vInvTx.begin(), vInvTx.end(), compareInvMempoolOrder);
// No reason to drain out at many times the network's capacity,
// especially since we have many peers and some will draw much shorter delays.
@@ -4189,6 +4406,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
@@ -4196,17 +4414,20 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
continue;
}
// Not in the mempool anymore? don't bother sending it.
- auto txinfo = m_mempool.info(hash);
+ auto txinfo = m_mempool.info(ToGenTxid(inv));
if (!txinfo.tx) {
continue;
}
+ auto txid = txinfo.tx->GetHash();
+ auto wtxid = txinfo.tx->GetWitnessHash();
// Peer told you to not send transactions at that feerate? Don't bother sending it.
if (txinfo.fee < filterrate.GetFee(txinfo.vsize)) {
continue;
}
if (pto->m_tx_relay->pfilter && !pto->m_tx_relay->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue;
// Send
- vInv.push_back(CInv(MSG_TX, hash));
+ State(pto->GetId())->m_recently_announced_invs.insert(hash);
+ vInv.push_back(inv);
nRelayedTransactions++;
{
// Expire old relay messages
@@ -4216,9 +4437,14 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
vRelayExpiration.pop_front();
}
- auto ret = mapRelay.insert(std::make_pair(hash, std::move(txinfo.tx)));
+ auto ret = mapRelay.emplace(txid, std::move(txinfo.tx));
if (ret.second) {
- vRelayExpiration.push_back(std::make_pair(nNow + std::chrono::microseconds{RELAY_TX_CACHE_TIME}.count(), ret.first));
+ vRelayExpiration.emplace_back(nNow + std::chrono::microseconds{RELAY_TX_CACHE_TIME}.count(), ret.first);
+ }
+ // Add wtxid-based lookup into mapRelay as well, so that peers can request by wtxid
+ auto ret2 = mapRelay.emplace(wtxid, ret.first->second);
+ if (ret2.second) {
+ vRelayExpiration.emplace_back(nNow + std::chrono::microseconds{RELAY_TX_CACHE_TIME}.count(), ret2.first);
}
}
if (vInv.size() == MAX_INV_SZ) {
@@ -4226,6 +4452,14 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
vInv.clear();
}
pto->m_tx_relay->filterInventoryKnown.insert(hash);
+ if (hash != txid) {
+ // Insert txid into filterInventoryKnown, even for
+ // wtxidrelay peers. This prevents re-adding of
+ // unconfirmed parents to the recently_announced
+ // filter, when a child tx is requested. See
+ // ProcessGetData().
+ pto->m_tx_relay->filterInventoryKnown.insert(txid);
+ }
}
}
}
@@ -4263,9 +4497,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
@@ -4275,7 +4509,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
@@ -4295,7 +4529,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
// Check that outbound peers have reasonable chains
// GetTime() is used by this anti-DoS logic so we can test this using mocktime
- ConsiderEviction(pto, GetTime());
+ ConsiderEviction(*pto, GetTime());
//
// Message: getdata (blocks)
@@ -4306,7 +4540,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
NodeId staller = -1;
FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller, consensusParams);
for (const CBlockIndex *pindex : vToDownload) {
- uint32_t nFetchFlags = GetFetchFlags(pto);
+ uint32_t nFetchFlags = GetFetchFlags(*pto);
vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash()));
MarkBlockAsInFlight(m_mempool, pto->GetId(), pindex->GetBlockHash(), pindex);
LogPrint(BCLog::NET, "Requesting block %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(),
@@ -4346,15 +4580,15 @@ 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(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);
@@ -4362,20 +4596,28 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
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
// (with a slight delay for inbound peers, to prefer
// requests to outbound peers).
- const auto next_process_time = CalculateTxGetDataTime(txid, current_time, !state.fPreferredDownload);
- tx_process_time.emplace(next_process_time, txid);
+ // Don't apply the txid-delay to re-requests of a
+ // transaction; the heuristic of delaying requests to
+ // txid-relay peers is to save bandwidth on initial
+ // announcement of a transaction, and doesn't make sense
+ // for a followup request if our first peer times out (and
+ // 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(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());
}
}
@@ -4386,15 +4628,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) {
@@ -4410,7 +4663,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
pto->m_tx_relay->nextSendTimeFeeFilter = timeNow + GetRandInt(MAX_FEEFILTER_CHANGE_DELAY) * 1000000;
}
}
- }
+ } // release cs_main
return true;
}
@@ -4422,6 +4675,7 @@ public:
// orphan transactions
mapOrphanTransactions.clear();
mapOrphanTransactionsByPrev.clear();
+ g_orphans_by_wtxid.clear();
}
};
static CNetProcessingCleanup instance_of_cnetprocessingcleanup;