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.cpp599
1 files changed, 369 insertions, 230 deletions
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 6d17ab4be8..c33dd29923 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -21,6 +21,7 @@
#include <node/blockstorage.h>
#include <policy/fees.h>
#include <policy/policy.h>
+#include <policy/settings.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
#include <random.h>
@@ -60,6 +61,8 @@ static constexpr auto UNCONDITIONAL_RELAY_DELAY = 2min;
* Timeout = base + per_header * (expected number of headers) */
static constexpr auto HEADERS_DOWNLOAD_TIMEOUT_BASE = 15min;
static constexpr auto HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER = 1ms;
+/** How long to wait for a peer to respond to a getheaders request */
+static constexpr auto HEADERS_RESPONSE_TIME{2min};
/** Protect at least this many outbound peers from disconnection due to slow/
* behind headers chain.
*/
@@ -291,7 +294,7 @@ struct Peer {
return m_tx_relay.get();
};
- TxRelay* GetTxRelay()
+ TxRelay* GetTxRelay() EXCLUSIVE_LOCKS_REQUIRED(!m_tx_relay_mutex)
{
return WITH_LOCK(m_tx_relay_mutex, return m_tx_relay.get());
};
@@ -354,6 +357,9 @@ struct Peer {
/** Work queue of items requested by this peer **/
std::deque<CInv> m_getdata_requests GUARDED_BY(m_getdata_requests_mutex);
+ /** Time of the last getheaders message to this peer */
+ std::atomic<NodeClock::time_point> m_last_getheaders_timestamp{NodeSeconds{}};
+
Peer(NodeId id)
: m_id{id}
{}
@@ -492,7 +498,7 @@ public:
void SendPings() override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
void RelayTransaction(const uint256& txid, const uint256& wtxid) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
void SetBestHeight(int height) override { m_best_height = height; };
- void Misbehaving(const NodeId pnode, const int howmuch, const std::string& message) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
+ void UnitTestMisbehaving(NodeId peer_id, int howmuch) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex) { Misbehaving(*Assert(GetPeerRef(peer_id)), howmuch, ""); };
void ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv,
const std::chrono::microseconds time_received, const std::atomic<bool>& interruptMsgProc) override
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_recent_confirmed_transactions_mutex, !m_most_recent_block_mutex);
@@ -500,7 +506,7 @@ public:
private:
/** Consider evicting an outbound peer based on the amount of time they've been behind our tip */
- void ConsiderEviction(CNode& pto, std::chrono::seconds time_in_seconds) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ void ConsiderEviction(CNode& pto, Peer& peer, std::chrono::seconds time_in_seconds) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** If we have extra outbound peers, try to disconnect the one with the oldest block announcement */
void EvictExtraOutboundPeers(std::chrono::seconds now) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@@ -517,6 +523,12 @@ private:
PeerRef RemovePeer(NodeId id) EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
/**
+ * 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(Peer& peer, int howmuch, const std::string& message);
+
+ /**
* 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
@@ -549,13 +561,28 @@ private:
void ProcessOrphanTx(std::set<uint256>& orphan_work_set) EXCLUSIVE_LOCKS_REQUIRED(cs_main, g_cs_orphans)
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
/** Process a single headers message from a peer. */
- void ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
+ void ProcessHeadersMessage(CNode& pfrom, Peer& peer,
const std::vector<CBlockHeader>& headers,
bool via_compact_block)
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
+ /** Various helpers for headers processing, invoked by ProcessHeadersMessage() */
+ /** Deal with state tracking and headers sync for peers that send the
+ * occasional non-connecting header (this can happen due to BIP 130 headers
+ * announcements for blocks interacting with the 2hr (MAX_FUTURE_BLOCK_TIME) rule). */
+ void HandleFewUnconnectingHeaders(CNode& pfrom, Peer& peer, const std::vector<CBlockHeader>& headers);
+ /** Return true if the headers connect to each other, false otherwise */
+ bool CheckHeadersAreContinuous(const std::vector<CBlockHeader>& headers) const;
+ /** Request further headers from this peer with a given locator.
+ * We don't issue a getheaders message if we have a recent one outstanding.
+ * This returns true if a getheaders is actually sent, and false otherwise.
+ */
+ bool MaybeSendGetHeaders(CNode& pfrom, const CBlockLocator& locator, Peer& peer);
+ /** Potentially fetch blocks from this peer upon receipt of a new headers tip */
+ void HeadersDirectFetchBlocks(CNode& pfrom, const CBlockIndex* pindexLast);
+ /** Update peer state based on received headers message */
+ void UpdatePeerStateForReceivedHeaders(CNode& pfrom, const CBlockIndex *pindexLast, bool received_new_header, bool may_have_more_headers);
- void SendBlockTransactions(CNode& pfrom, const CBlock& block, const BlockTransactionsRequest& req)
- EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
+ void SendBlockTransactions(CNode& pfrom, Peer& peer, const CBlock& block, const BlockTransactionsRequest& req);
/** Register with TxRequestTracker that an INV has been received from a
* peer. The announcement parameters are decided in PeerManager and then
@@ -602,9 +629,11 @@ private:
/** Next time to check for stale tip */
std::chrono::seconds m_stale_tip_check_time{0s};
- /** Whether this node is running in blocks only mode */
+ /** Whether this node is running in -blocksonly mode */
const bool m_ignore_incoming_txs;
+ bool RejectIncomingTxs(const CNode& peer) const;
+
/** Whether we've completed initial sync yet, for determining when to turn
* on extra block-relay-only peers. */
bool m_initial_sync_finished{false};
@@ -1008,7 +1037,7 @@ void PeerManagerImpl::MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid)
{
AssertLockHeld(cs_main);
- // Never request high-bandwidth mode from peers if we're blocks-only. Our
+ // When in -blocksonly mode, never request high-bandwidth mode from peers. Our
// mempool will not contain the transactions necessary to reconstruct the
// compact block.
if (m_ignore_incoming_txs) return;
@@ -1441,33 +1470,31 @@ void PeerManagerImpl::AddToCompactExtraTransactions(const CTransactionRef& tx)
vExtraTxnForCompactIt = (vExtraTxnForCompactIt + 1) % max_extra_txn;
}
-void PeerManagerImpl::Misbehaving(const NodeId pnode, const int howmuch, const std::string& message)
+void PeerManagerImpl::Misbehaving(Peer& peer, int howmuch, const std::string& message)
{
assert(howmuch > 0);
- PeerRef peer = GetPeerRef(pnode);
- if (peer == nullptr) return;
-
- LOCK(peer->m_misbehavior_mutex);
- const int score_before{peer->m_misbehavior_score};
- peer->m_misbehavior_score += howmuch;
- const int score_now{peer->m_misbehavior_score};
+ LOCK(peer.m_misbehavior_mutex);
+ const int score_before{peer.m_misbehavior_score};
+ peer.m_misbehavior_score += howmuch;
+ const int score_now{peer.m_misbehavior_score};
const std::string message_prefixed = message.empty() ? "" : (": " + message);
std::string warning;
if (score_now >= DISCOURAGEMENT_THRESHOLD && score_before < DISCOURAGEMENT_THRESHOLD) {
warning = " DISCOURAGE THRESHOLD EXCEEDED";
- peer->m_should_discourage = true;
+ peer.m_should_discourage = true;
}
LogPrint(BCLog::NET, "Misbehaving: peer=%d (%d -> %d)%s%s\n",
- pnode, score_before, score_now, warning, message_prefixed);
+ peer.m_id, score_before, score_now, warning, message_prefixed);
}
bool PeerManagerImpl::MaybePunishNodeForBlock(NodeId nodeid, const BlockValidationState& state,
bool via_compact_block, const std::string& message)
{
+ PeerRef peer{GetPeerRef(nodeid)};
switch (state.GetResult()) {
case BlockValidationResult::BLOCK_RESULT_UNSET:
break;
@@ -1475,7 +1502,7 @@ bool PeerManagerImpl::MaybePunishNodeForBlock(NodeId nodeid, const BlockValidati
case BlockValidationResult::BLOCK_CONSENSUS:
case BlockValidationResult::BLOCK_MUTATED:
if (!via_compact_block) {
- Misbehaving(nodeid, 100, message);
+ if (peer) Misbehaving(*peer, 100, message);
return true;
}
break;
@@ -1490,7 +1517,7 @@ bool PeerManagerImpl::MaybePunishNodeForBlock(NodeId nodeid, const BlockValidati
// Discourage outbound (but not inbound) peers if on an invalid chain.
// Exempt HB compact block peers. Manual connections are always protected from discouragement.
if (!via_compact_block && !node_state->m_is_inbound) {
- Misbehaving(nodeid, 100, message);
+ if (peer) Misbehaving(*peer, 100, message);
return true;
}
break;
@@ -1498,12 +1525,12 @@ bool PeerManagerImpl::MaybePunishNodeForBlock(NodeId nodeid, const BlockValidati
case BlockValidationResult::BLOCK_INVALID_HEADER:
case BlockValidationResult::BLOCK_CHECKPOINT:
case BlockValidationResult::BLOCK_INVALID_PREV:
- Misbehaving(nodeid, 100, message);
+ if (peer) Misbehaving(*peer, 100, message);
return true;
// Conflicting (but not necessarily invalid) data or different policy:
case BlockValidationResult::BLOCK_MISSING_PREV:
// TODO: Handle this much more gracefully (10 DoS points is super arbitrary)
- Misbehaving(nodeid, 10, message);
+ if (peer) Misbehaving(*peer, 10, message);
return true;
case BlockValidationResult::BLOCK_RECENT_CONSENSUS_CHANGE:
case BlockValidationResult::BLOCK_TIME_FUTURE:
@@ -1517,12 +1544,13 @@ bool PeerManagerImpl::MaybePunishNodeForBlock(NodeId nodeid, const BlockValidati
bool PeerManagerImpl::MaybePunishNodeForTx(NodeId nodeid, const TxValidationState& state, const std::string& message)
{
+ PeerRef peer{GetPeerRef(nodeid)};
switch (state.GetResult()) {
case TxValidationResult::TX_RESULT_UNSET:
break;
// The node is providing invalid data:
case TxValidationResult::TX_CONSENSUS:
- Misbehaving(nodeid, 100, message);
+ if (peer) Misbehaving(*peer, 100, message);
return true;
// Conflicting (but not necessarily invalid) data or different policy:
case TxValidationResult::TX_RECENT_CONSENSUS_CHANGE:
@@ -2172,12 +2200,12 @@ uint32_t PeerManagerImpl::GetFetchFlags(const CNode& pfrom) const EXCLUSIVE_LOCK
return nFetchFlags;
}
-void PeerManagerImpl::SendBlockTransactions(CNode& pfrom, const CBlock& block, const BlockTransactionsRequest& req)
+void PeerManagerImpl::SendBlockTransactions(CNode& pfrom, Peer& peer, const CBlock& block, const BlockTransactionsRequest& req)
{
BlockTransactions resp(req);
for (size_t i = 0; i < req.indexes.size(); i++) {
if (req.indexes[i] >= block.vtx.size()) {
- Misbehaving(pfrom.GetId(), 100, "getblocktxn with out-of-bounds tx indices");
+ Misbehaving(peer, 100, "getblocktxn with out-of-bounds tx indices");
return;
}
resp.txn[i] = block.vtx[req.indexes[i]];
@@ -2187,7 +2215,204 @@ void PeerManagerImpl::SendBlockTransactions(CNode& pfrom, const CBlock& block, c
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::BLOCKTXN, resp));
}
-void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
+/**
+ * Special handling for unconnecting headers that might be part of a block
+ * announcement.
+ *
+ * We'll send a getheaders message in response to try to connect the chain.
+ *
+ * The peer can send up to MAX_UNCONNECTING_HEADERS in a row that
+ * don't connect before given DoS points.
+ *
+ * Once a headers message is received that is valid and does connect,
+ * nUnconnectingHeaders gets reset back to 0.
+ */
+void PeerManagerImpl::HandleFewUnconnectingHeaders(CNode& pfrom, Peer& peer,
+ const std::vector<CBlockHeader>& headers)
+{
+ const CNetMsgMaker msgMaker(pfrom.GetCommonVersion());
+
+ LOCK(cs_main);
+ CNodeState *nodestate = State(pfrom.GetId());
+
+ nodestate->nUnconnectingHeaders++;
+ // Try to fill in the missing headers.
+ if (MaybeSendGetHeaders(pfrom, m_chainman.ActiveChain().GetLocator(m_chainman.m_best_header), peer)) {
+ 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(),
+ m_chainman.m_best_header->nHeight,
+ 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());
+
+ // The peer may just be broken, so periodically assign DoS points if this
+ // condition persists.
+ if (nodestate->nUnconnectingHeaders % MAX_UNCONNECTING_HEADERS == 0) {
+ Misbehaving(peer, 20, strprintf("%d non-connecting headers", nodestate->nUnconnectingHeaders));
+ }
+}
+
+bool PeerManagerImpl::CheckHeadersAreContinuous(const std::vector<CBlockHeader>& headers) const
+{
+ uint256 hashLastBlock;
+ for (const CBlockHeader& header : headers) {
+ if (!hashLastBlock.IsNull() && header.hashPrevBlock != hashLastBlock) {
+ return false;
+ }
+ hashLastBlock = header.GetHash();
+ }
+ return true;
+}
+
+bool PeerManagerImpl::MaybeSendGetHeaders(CNode& pfrom, const CBlockLocator& locator, Peer& peer)
+{
+ const CNetMsgMaker msgMaker(pfrom.GetCommonVersion());
+
+ const auto current_time = NodeClock::now();
+
+ // Only allow a new getheaders message to go out if we don't have a recent
+ // one already in-flight
+ if (current_time - peer.m_last_getheaders_timestamp.load() > HEADERS_RESPONSE_TIME) {
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, locator, uint256()));
+ peer.m_last_getheaders_timestamp = current_time;
+ return true;
+ }
+ return false;
+}
+
+/*
+ * Given a new headers tip ending in pindexLast, potentially request blocks towards that tip.
+ * We require that the given tip have at least as much work as our tip, and for
+ * our current tip to be "close to synced" (see CanDirectFetch()).
+ */
+void PeerManagerImpl::HeadersDirectFetchBlocks(CNode& pfrom, const CBlockIndex* pindexLast)
+{
+ const CNetMsgMaker msgMaker(pfrom.GetCommonVersion());
+
+ LOCK(cs_main);
+ CNodeState *nodestate = State(pfrom.GetId());
+
+ if (CanDirectFetch() && pindexLast->IsValid(BLOCK_VALID_TREE) && m_chainman.ActiveChain().Tip()->nChainWork <= pindexLast->nChainWork) {
+
+ std::vector<const CBlockIndex*> vToFetch;
+ const CBlockIndex *pindexWalk = pindexLast;
+ // Calculate all the blocks we'd need to switch to pindexLast, up to a limit.
+ while (pindexWalk && !m_chainman.ActiveChain().Contains(pindexWalk) && vToFetch.size() <= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
+ if (!(pindexWalk->nStatus & BLOCK_HAVE_DATA) &&
+ !IsBlockRequested(pindexWalk->GetBlockHash()) &&
+ (!DeploymentActiveAt(*pindexWalk, m_chainman, Consensus::DEPLOYMENT_SEGWIT) || State(pfrom.GetId())->fHaveWitness)) {
+ // We don't have this block, and it's not yet in flight.
+ vToFetch.push_back(pindexWalk);
+ }
+ pindexWalk = pindexWalk->pprev;
+ }
+ // If pindexWalk still isn't on our main chain, we're looking at a
+ // very large reorg at a time we think we're close to caught up to
+ // the main chain -- this shouldn't really happen. Bail out on the
+ // direct fetch and rely on parallel download instead.
+ if (!m_chainman.ActiveChain().Contains(pindexWalk)) {
+ LogPrint(BCLog::NET, "Large reorg, won't direct fetch to %s (%d)\n",
+ pindexLast->GetBlockHash().ToString(),
+ pindexLast->nHeight);
+ } else {
+ std::vector<CInv> vGetData;
+ // Download as much as possible, from earliest to latest.
+ for (const CBlockIndex *pindex : reverse_iterate(vToFetch)) {
+ if (nodestate->nBlocksInFlight >= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
+ // Can't download any more from this peer
+ break;
+ }
+ uint32_t nFetchFlags = GetFetchFlags(pfrom);
+ vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash()));
+ BlockRequested(pfrom.GetId(), *pindex);
+ LogPrint(BCLog::NET, "Requesting block %s from peer=%d\n",
+ pindex->GetBlockHash().ToString(), pfrom.GetId());
+ }
+ if (vGetData.size() > 1) {
+ LogPrint(BCLog::NET, "Downloading blocks toward %s (%d) via headers direct fetch\n",
+ pindexLast->GetBlockHash().ToString(), pindexLast->nHeight);
+ }
+ if (vGetData.size() > 0) {
+ if (!m_ignore_incoming_txs &&
+ nodestate->m_provides_cmpctblocks &&
+ vGetData.size() == 1 &&
+ mapBlocksInFlight.size() == 1 &&
+ pindexLast->pprev->IsValid(BLOCK_VALID_CHAIN)) {
+ // In any case, we want to download using a compact block, not a regular one
+ vGetData[0] = CInv(MSG_CMPCT_BLOCK, vGetData[0].hash);
+ }
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, vGetData));
+ }
+ }
+ }
+}
+
+/**
+ * Given receipt of headers from a peer ending in pindexLast, along with
+ * whether that header was new and whether the headers message was full,
+ * update the state we keep for the peer.
+ */
+void PeerManagerImpl::UpdatePeerStateForReceivedHeaders(CNode& pfrom,
+ const CBlockIndex *pindexLast, bool received_new_header, bool may_have_more_headers)
+{
+ LOCK(cs_main);
+ CNodeState *nodestate = State(pfrom.GetId());
+ if (nodestate->nUnconnectingHeaders > 0) {
+ LogPrint(BCLog::NET, "peer=%d: resetting nUnconnectingHeaders (%d -> 0)\n", pfrom.GetId(), nodestate->nUnconnectingHeaders);
+ }
+ nodestate->nUnconnectingHeaders = 0;
+
+ assert(pindexLast);
+ UpdateBlockAvailability(pfrom.GetId(), pindexLast->GetBlockHash());
+
+ // From here, pindexBestKnownBlock should be guaranteed to be non-null,
+ // because it is set in UpdateBlockAvailability. Some nullptr checks
+ // are still present, however, as belt-and-suspenders.
+
+ if (received_new_header && pindexLast->nChainWork > m_chainman.ActiveChain().Tip()->nChainWork) {
+ nodestate->m_last_block_announcement = GetTime();
+ }
+
+ // If we're in IBD, we want outbound peers that will serve us a useful
+ // chain. Disconnect peers that are on chains with insufficient work.
+ if (m_chainman.ActiveChainstate().IsInitialBlockDownload() && !may_have_more_headers) {
+ // If the peer has no more headers to give us, then we know we have
+ // their tip.
+ if (nodestate->pindexBestKnownBlock && nodestate->pindexBestKnownBlock->nChainWork < nMinimumChainWork) {
+ // This peer has too little work on their headers chain to help
+ // us sync -- disconnect if it is an outbound disconnection
+ // candidate.
+ // Note: We compare their tip to nMinimumChainWork (rather than
+ // m_chainman.ActiveChain().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 (pfrom.IsOutboundOrBlockRelayConn()) {
+ LogPrintf("Disconnecting outbound peer %d -- headers chain has insufficient work\n", pfrom.GetId());
+ pfrom.fDisconnect = true;
+ }
+ }
+ }
+
+ // If this is an outbound full-relay peer, check to see if we should protect
+ // it from the bad/lagging chain logic.
+ // Note that outbound block-relay peers are excluded from this protection, and
+ // thus always subject to eviction under the bad/lagging chain logic.
+ // See ChainSyncTimeoutState.
+ if (!pfrom.fDisconnect && pfrom.IsFullOutboundConn() && nodestate->pindexBestKnownBlock != nullptr) {
+ if (m_outbound_peers_with_protect_from_disconnect < MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT && nodestate->pindexBestKnownBlock->nChainWork >= m_chainman.ActiveChain().Tip()->nChainWork && !nodestate->m_chain_sync.m_protect) {
+ LogPrint(BCLog::NET, "Protecting outbound peer=%d from eviction\n", pfrom.GetId());
+ nodestate->m_chain_sync.m_protect = true;
+ ++m_outbound_peers_with_protect_from_disconnect;
+ }
+ }
+}
+
+void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, Peer& peer,
const std::vector<CBlockHeader>& headers,
bool via_compact_block)
{
@@ -2199,55 +2424,33 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
return;
}
- bool received_new_header = false;
const CBlockIndex *pindexLast = nullptr;
- {
- LOCK(cs_main);
- 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
- // don't connect:
- // - Send a getheaders message in response to try to connect the chain.
- // - The peer can send up to MAX_UNCONNECTING_HEADERS in a row that
- // don't connect before giving DoS points
- // - Once a headers message is received that is valid and does connect,
- // nUnconnectingHeaders gets reset back to 0.
- if (!m_chainman.m_blockman.LookupBlockIndex(headers[0].hashPrevBlock) && nCount < MAX_BLOCKS_TO_ANNOUNCE) {
- nodestate->nUnconnectingHeaders++;
- m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, m_chainman.ActiveChain().GetLocator(m_chainman.m_best_header), 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(),
- m_chainman.m_best_header->nHeight,
- 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());
-
- if (nodestate->nUnconnectingHeaders % MAX_UNCONNECTING_HEADERS == 0) {
- Misbehaving(pfrom.GetId(), 20, strprintf("%d non-connecting headers", nodestate->nUnconnectingHeaders));
- }
- return;
- }
+ // Do these headers connect to something in our block index?
+ bool headers_connect_blockindex{WITH_LOCK(::cs_main, return m_chainman.m_blockman.LookupBlockIndex(headers[0].hashPrevBlock) != nullptr)};
- uint256 hashLastBlock;
- for (const CBlockHeader& header : headers) {
- if (!hashLastBlock.IsNull() && header.hashPrevBlock != hashLastBlock) {
- Misbehaving(pfrom.GetId(), 20, "non-continuous headers sequence");
- return;
- }
- hashLastBlock = header.GetHash();
+ if (!headers_connect_blockindex) {
+ if (nCount <= MAX_BLOCKS_TO_ANNOUNCE) {
+ // If this looks like it could be a BIP 130 block announcement, use
+ // special logic for handling headers that don't connect, as this
+ // could be benign.
+ HandleFewUnconnectingHeaders(pfrom, peer, headers);
+ } else {
+ Misbehaving(peer, 10, "invalid header received");
}
+ return;
+ }
- // If we don't have the last header, then they'll have given us
- // something new (if these headers are valid).
- if (!m_chainman.m_blockman.LookupBlockIndex(hashLastBlock)) {
- received_new_header = true;
- }
+ // At this point, the headers connect to something in our block index.
+ if (!CheckHeadersAreContinuous(headers)) {
+ Misbehaving(peer, 20, "non-continuous headers sequence");
+ return;
}
+ // If we don't have the last header, then this peer will have given us
+ // something new (if these headers are valid).
+ bool received_new_header{WITH_LOCK(::cs_main, return m_chainman.m_blockman.LookupBlockIndex(headers.back().GetHash()) == nullptr)};
+
BlockValidationState state;
if (!m_chainman.ProcessNewBlockHeaders(headers, state, &pindexLast)) {
if (state.IsInvalid()) {
@@ -2256,123 +2459,20 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
}
}
- {
- LOCK(cs_main);
- CNodeState *nodestate = State(pfrom.GetId());
- if (nodestate->nUnconnectingHeaders > 0) {
- LogPrint(BCLog::NET, "peer=%d: resetting nUnconnectingHeaders (%d -> 0)\n", pfrom.GetId(), nodestate->nUnconnectingHeaders);
- }
- nodestate->nUnconnectingHeaders = 0;
-
- assert(pindexLast);
- UpdateBlockAvailability(pfrom.GetId(), pindexLast->GetBlockHash());
-
- // From here, pindexBestKnownBlock should be guaranteed to be non-null,
- // because it is set in UpdateBlockAvailability. Some nullptr checks
- // are still present, however, as belt-and-suspenders.
-
- if (received_new_header && pindexLast->nChainWork > m_chainman.ActiveChain().Tip()->nChainWork) {
- nodestate->m_last_block_announcement = GetTime();
- }
-
- if (nCount == MAX_HEADERS_RESULTS) {
- // Headers message had its maximum size; the peer may have more headers.
- // TODO: optimize: if pindexLast is an ancestor of m_chainman.ActiveChain().Tip or m_chainman.m_best_header, continue
- // from there instead.
+ // Consider fetching more headers.
+ if (nCount == MAX_HEADERS_RESULTS) {
+ // Headers message had its maximum size; the peer may have more headers.
+ if (MaybeSendGetHeaders(pfrom, m_chainman.ActiveChain().GetLocator(pindexLast), peer)) {
LogPrint(BCLog::NET, "more getheaders (%d) to end to peer=%d (startheight:%d)\n",
- pindexLast->nHeight, pfrom.GetId(), peer.m_starting_height);
- m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, m_chainman.ActiveChain().GetLocator(pindexLast), uint256()));
- }
-
- // If this set of headers is valid and ends in a block with at least as
- // much work as our tip, download as much as possible.
- if (CanDirectFetch() && pindexLast->IsValid(BLOCK_VALID_TREE) && m_chainman.ActiveChain().Tip()->nChainWork <= pindexLast->nChainWork) {
- std::vector<const CBlockIndex*> vToFetch;
- const CBlockIndex *pindexWalk = pindexLast;
- // Calculate all the blocks we'd need to switch to pindexLast, up to a limit.
- while (pindexWalk && !m_chainman.ActiveChain().Contains(pindexWalk) && vToFetch.size() <= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
- if (!(pindexWalk->nStatus & BLOCK_HAVE_DATA) &&
- !IsBlockRequested(pindexWalk->GetBlockHash()) &&
- (!DeploymentActiveAt(*pindexWalk, m_chainman, Consensus::DEPLOYMENT_SEGWIT) || State(pfrom.GetId())->fHaveWitness)) {
- // We don't have this block, and it's not yet in flight.
- vToFetch.push_back(pindexWalk);
- }
- pindexWalk = pindexWalk->pprev;
- }
- // If pindexWalk still isn't on our main chain, we're looking at a
- // very large reorg at a time we think we're close to caught up to
- // the main chain -- this shouldn't really happen. Bail out on the
- // direct fetch and rely on parallel download instead.
- if (!m_chainman.ActiveChain().Contains(pindexWalk)) {
- LogPrint(BCLog::NET, "Large reorg, won't direct fetch to %s (%d)\n",
- pindexLast->GetBlockHash().ToString(),
- pindexLast->nHeight);
- } else {
- std::vector<CInv> vGetData;
- // Download as much as possible, from earliest to latest.
- for (const CBlockIndex *pindex : reverse_iterate(vToFetch)) {
- if (nodestate->nBlocksInFlight >= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
- // Can't download any more from this peer
- break;
- }
- uint32_t nFetchFlags = GetFetchFlags(pfrom);
- vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash()));
- BlockRequested(pfrom.GetId(), *pindex);
- LogPrint(BCLog::NET, "Requesting block %s from peer=%d\n",
- pindex->GetBlockHash().ToString(), pfrom.GetId());
- }
- if (vGetData.size() > 1) {
- LogPrint(BCLog::NET, "Downloading blocks toward %s (%d) via headers direct fetch\n",
- pindexLast->GetBlockHash().ToString(), pindexLast->nHeight);
- }
- if (vGetData.size() > 0) {
- if (!m_ignore_incoming_txs &&
- nodestate->m_provides_cmpctblocks &&
- vGetData.size() == 1 &&
- mapBlocksInFlight.size() == 1 &&
- pindexLast->pprev->IsValid(BLOCK_VALID_CHAIN)) {
- // In any case, we want to download using a compact block, not a regular one
- vGetData[0] = CInv(MSG_CMPCT_BLOCK, vGetData[0].hash);
- }
- m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, vGetData));
- }
- }
- }
- // If we're in IBD, we want outbound peers that will serve us a useful
- // chain. Disconnect peers that are on chains with insufficient work.
- if (m_chainman.ActiveChainstate().IsInitialBlockDownload() && nCount != MAX_HEADERS_RESULTS) {
- // When nCount < MAX_HEADERS_RESULTS, we know we have no more
- // 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 it is an outbound disconnection
- // candidate.
- // Note: We compare their tip to nMinimumChainWork (rather than
- // m_chainman.ActiveChain().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 (pfrom.IsOutboundOrBlockRelayConn()) {
- LogPrintf("Disconnecting outbound peer %d -- headers chain has insufficient work\n", pfrom.GetId());
- pfrom.fDisconnect = true;
- }
- }
- }
-
- // If this is an outbound full-relay peer, check to see if we should protect
- // it from the bad/lagging chain logic.
- // Note that outbound block-relay peers are excluded from this protection, and
- // thus always subject to eviction under the bad/lagging chain logic.
- // See ChainSyncTimeoutState.
- if (!pfrom.fDisconnect && pfrom.IsFullOutboundConn() && nodestate->pindexBestKnownBlock != nullptr) {
- if (m_outbound_peers_with_protect_from_disconnect < MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT && nodestate->pindexBestKnownBlock->nChainWork >= m_chainman.ActiveChain().Tip()->nChainWork && !nodestate->m_chain_sync.m_protect) {
- LogPrint(BCLog::NET, "Protecting outbound peer=%d from eviction\n", pfrom.GetId());
- nodestate->m_chain_sync.m_protect = true;
- ++m_outbound_peers_with_protect_from_disconnect;
- }
+ pindexLast->nHeight, pfrom.GetId(), peer.m_starting_height);
}
}
+ UpdatePeerStateForReceivedHeaders(pfrom, pindexLast, received_new_header, nCount == MAX_HEADERS_RESULTS);
+
+ // Consider immediately downloading blocks.
+ HeadersDirectFetchBlocks(pfrom, pindexLast);
+
return;
}
@@ -2998,7 +3098,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
if (vAddr.size() > MAX_ADDR_TO_SEND)
{
- Misbehaving(pfrom.GetId(), 20, strprintf("%s message size = %u", msg_type, vAddr.size()));
+ Misbehaving(*peer, 20, strprintf("%s message size = %u", msg_type, vAddr.size()));
return;
}
@@ -3012,7 +3112,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
if (peer->m_addr_token_bucket < MAX_ADDR_PROCESSING_TOKEN_BUCKET) {
// Don't increment bucket if it's already full
const auto time_diff = std::max(current_time - peer->m_addr_token_timestamp, 0us);
- const double increment = CountSecondsDouble(time_diff) * MAX_ADDR_RATE_PER_SECOND;
+ const double increment = Ticks<SecondsDouble>(time_diff) * MAX_ADDR_RATE_PER_SECOND;
peer->m_addr_token_bucket = std::min<double>(peer->m_addr_token_bucket + increment, MAX_ADDR_PROCESSING_TOKEN_BUCKET);
}
peer->m_addr_token_timestamp = current_time;
@@ -3079,18 +3179,11 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
vRecv >> vInv;
if (vInv.size() > MAX_INV_SZ)
{
- Misbehaving(pfrom.GetId(), 20, strprintf("inv message size = %u", vInv.size()));
+ Misbehaving(*peer, 20, strprintf("inv message size = %u", vInv.size()));
return;
}
- // Reject tx INVs when the -blocksonly setting is enabled, or this is a
- // block-relay-only peer
- bool reject_tx_invs{m_ignore_incoming_txs || pfrom.IsBlockOnlyConn()};
-
- // Allow peers with relay permission to send data other than blocks in blocks only mode
- if (pfrom.HasPermission(NetPermissionFlags::Relay)) {
- reject_tx_invs = false;
- }
+ const bool reject_tx_invs{RejectIncomingTxs(pfrom)};
LOCK(cs_main);
@@ -3142,8 +3235,11 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
}
if (best_block != nullptr) {
- m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, m_chainman.ActiveChain().GetLocator(m_chainman.m_best_header), *best_block));
- LogPrint(BCLog::NET, "getheaders (%d) %s to peer=%d\n", m_chainman.m_best_header->nHeight, best_block->ToString(), pfrom.GetId());
+ if (MaybeSendGetHeaders(pfrom, m_chainman.ActiveChain().GetLocator(m_chainman.m_best_header), *peer)) {
+ LogPrint(BCLog::NET, "getheaders (%d) %s to peer=%d\n",
+ m_chainman.m_best_header->nHeight, best_block->ToString(),
+ pfrom.GetId());
+ }
}
return;
@@ -3154,7 +3250,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
vRecv >> vInv;
if (vInv.size() > MAX_INV_SZ)
{
- Misbehaving(pfrom.GetId(), 20, strprintf("getdata message size = %u", vInv.size()));
+ Misbehaving(*peer, 20, strprintf("getdata message size = %u", vInv.size()));
return;
}
@@ -3252,7 +3348,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// Unlock m_most_recent_block_mutex to avoid cs_main lock inversion
}
if (recent_block) {
- SendBlockTransactions(pfrom, *recent_block, req);
+ SendBlockTransactions(pfrom, *peer, *recent_block, req);
return;
}
@@ -3270,7 +3366,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
bool ret = ReadBlockFromDisk(block, pindex, m_chainparams.GetConsensus());
assert(ret);
- SendBlockTransactions(pfrom, block, req);
+ SendBlockTransactions(pfrom, *peer, block, req);
return;
}
}
@@ -3316,7 +3412,10 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// others.
if (m_chainman.ActiveTip() == nullptr ||
(m_chainman.ActiveTip()->nChainWork < nMinimumChainWork && !pfrom.HasPermission(NetPermissionFlags::Download))) {
- LogPrint(BCLog::NET, "Ignoring getheaders from peer=%d because active chain has too little work\n", pfrom.GetId());
+ LogPrint(BCLog::NET, "Ignoring getheaders from peer=%d because active chain has too little work; sending empty response\n", pfrom.GetId());
+ // Just respond with an empty headers message, to tell the peer to
+ // go away but not treat us as unresponsive.
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::HEADERS, std::vector<CBlock>()));
return;
}
@@ -3371,10 +3470,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
}
if (msg_type == NetMsgType::TX) {
- // Stop processing the transaction early if
- // 1) We are in blocks only mode and peer has no relay permission; OR
- // 2) This peer is a block-relay-only peer
- if ((m_ignore_incoming_txs && !pfrom.HasPermission(NetPermissionFlags::Relay)) || pfrom.IsBlockOnlyConn()) {
+ if (RejectIncomingTxs(pfrom)) {
LogPrint(BCLog::NET, "transaction sent in violation of protocol peer=%d\n", pfrom.GetId());
pfrom.fDisconnect = true;
return;
@@ -3600,8 +3696,9 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
if (!m_chainman.m_blockman.LookupBlockIndex(cmpctblock.header.hashPrevBlock)) {
// Doesn't connect (or is genesis), instead of DoSing in AcceptBlockHeader, request deeper headers
- if (!m_chainman.ActiveChainstate().IsInitialBlockDownload())
- m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, m_chainman.ActiveChain().GetLocator(m_chainman.m_best_header), uint256()));
+ if (!m_chainman.ActiveChainstate().IsInitialBlockDownload()) {
+ MaybeSendGetHeaders(pfrom, m_chainman.ActiveChain().GetLocator(m_chainman.m_best_header), *peer);
+ }
return;
}
@@ -3692,7 +3789,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
ReadStatus status = partialBlock.InitData(cmpctblock, vExtraTxnForCompact);
if (status == READ_STATUS_INVALID) {
RemoveBlockRequest(pindex->GetBlockHash()); // Reset in-flight state in case Misbehaving does not result in a disconnect
- Misbehaving(pfrom.GetId(), 100, "invalid compact block");
+ Misbehaving(*peer, 100, "invalid compact block");
return;
} else if (status == READ_STATUS_FAILED) {
// Duplicate txindexes, the block is now in-flight, so just request it
@@ -3819,7 +3916,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
ReadStatus status = partialBlock.FillBlock(*pblock, resp.txn);
if (status == READ_STATUS_INVALID) {
RemoveBlockRequest(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");
+ Misbehaving(*peer, 100, "invalid compact block/non-matching block transactions");
return;
} else if (status == READ_STATUS_FAILED) {
// Might have collided, fall back to getdata now :(
@@ -3875,12 +3972,16 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
return;
}
+ // Assume that this is in response to any outstanding getheaders
+ // request we may have sent, and clear out the time of our last request
+ peer->m_last_getheaders_timestamp.store(NodeSeconds{});
+
std::vector<CBlockHeader> headers;
// Bypass the normal CBlock deserialization, as we don't want to risk deserializing 2000 full blocks.
unsigned int nCount = ReadCompactSize(vRecv);
if (nCount > MAX_HEADERS_RESULTS) {
- Misbehaving(pfrom.GetId(), 20, strprintf("headers message size = %u", nCount));
+ Misbehaving(*peer, 20, strprintf("headers message size = %u", nCount));
return;
}
headers.resize(nCount);
@@ -4074,7 +4175,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
if (!filter.IsWithinSizeConstraints())
{
// There is no excuse for sending a too-large filter
- Misbehaving(pfrom.GetId(), 100, "too-large bloom filter");
+ Misbehaving(*peer, 100, "too-large bloom filter");
} else if (auto tx_relay = peer->GetTxRelay(); tx_relay != nullptr) {
{
LOCK(tx_relay->m_bloom_filter_mutex);
@@ -4082,6 +4183,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
tx_relay->m_relay_txs = true;
}
pfrom.m_bloom_filter_loaded = true;
+ pfrom.m_relays_txs = true;
}
return;
}
@@ -4109,7 +4211,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
}
}
if (bad) {
- Misbehaving(pfrom.GetId(), 100, "bad filteradd message");
+ Misbehaving(*peer, 100, "bad filteradd message");
}
return;
}
@@ -4302,7 +4404,7 @@ bool PeerManagerImpl::ProcessMessages(CNode* pfrom, std::atomic<bool>& interrupt
return fMoreWork;
}
-void PeerManagerImpl::ConsiderEviction(CNode& pto, std::chrono::seconds time_in_seconds)
+void PeerManagerImpl::ConsiderEviction(CNode& pto, Peer& peer, std::chrono::seconds time_in_seconds)
{
AssertLockHeld(cs_main);
@@ -4340,10 +4442,15 @@ void PeerManagerImpl::ConsiderEviction(CNode& pto, std::chrono::seconds time_in_
pto.fDisconnect = true;
} else {
assert(state.m_chain_sync.m_work_header);
+ // Here, we assume that the getheaders message goes out,
+ // because it'll either go out or be skipped because of a
+ // getheaders in-flight already, in which case the peer should
+ // still respond to us with a sufficiently high work chain tip.
+ MaybeSendGetHeaders(pto,
+ m_chainman.ActiveChain().GetLocator(state.m_chain_sync.m_work_header->pprev),
+ peer);
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());
- m_connman.PushMessage(&pto, msgMaker.Make(NetMsgType::GETHEADERS, m_chainman.ActiveChain().GetLocator(state.m_chain_sync.m_work_header->pprev), uint256()));
state.m_chain_sync.m_sent_getheaders = true;
- constexpr auto HEADERS_RESPONSE_TIME{2min};
// Bump the timeout to allow a response, which could clear the timeout
// (if the response shows the peer has synced), reset the timeout (if
// the peer syncs to the required work but not to our tip), or result
@@ -4606,7 +4713,7 @@ void PeerManagerImpl::MaybeSendFeefilter(CNode& pto, Peer& peer, std::chrono::mi
// transactions to us, regardless of feefilter state.
if (pto.IsBlockOnlyConn()) return;
- CAmount currentFilter = m_mempool.GetMinFee(gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFeePerK();
+ CAmount currentFilter = m_mempool.GetMinFee().GetFeePerK();
static FeeFilterRounder g_filter_rounder{CFeeRate{DEFAULT_MIN_RELAY_TX_FEE}};
if (m_chainman.ActiveChainstate().IsInitialBlockDownload()) {
@@ -4642,7 +4749,7 @@ void PeerManagerImpl::MaybeSendFeefilter(CNode& pto, Peer& peer, std::chrono::mi
namespace {
class CompareInvMempoolOrder
{
- CTxMemPool *mp;
+ CTxMemPool* mp;
bool m_wtxid_relay;
public:
explicit CompareInvMempoolOrder(CTxMemPool *_mempool, bool use_wtxid)
@@ -4658,6 +4765,15 @@ public:
return mp->CompareDepthAndScore(*b, *a, m_wtxid_relay);
}
};
+} // namespace
+
+bool PeerManagerImpl::RejectIncomingTxs(const CNode& peer) const
+{
+ // block-relay-only peers may never send txs to us
+ if (peer.IsBlockOnlyConn()) return true;
+ // In -blocksonly mode, peers need the 'relay' permission to send txs to us
+ if (m_ignore_incoming_txs && !peer.HasPermission(NetPermissionFlags::Relay)) return true;
+ return false;
}
bool PeerManagerImpl::SetupAddressRelay(const CNode& node, Peer& peer)
@@ -4717,19 +4833,31 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
if (m_chainman.m_best_header == nullptr) {
m_chainman.m_best_header = m_chainman.ActiveChain().Tip();
}
- bool fFetch = state.fPreferredDownload || (m_num_preferred_download_peers == 0 && !pto->fClient && !pto->IsAddrFetchConn()); // Download if this is a nice peer, or we have no nice peers and this one might do.
+
+ // Determine whether we might try initial headers sync or parallel
+ // block download from this peer -- this mostly affects behavior while
+ // in IBD (once out of IBD, we sync from all peers).
+ bool sync_blocks_and_headers_from_peer = false;
+ if (state.fPreferredDownload) {
+ sync_blocks_and_headers_from_peer = true;
+ } else if (!pto->fClient && !pto->IsAddrFetchConn()) {
+ // Typically this is an inbound peer. If we don't have any outbound
+ // peers, or if we aren't downloading any blocks from such peers,
+ // then allow block downloads from this peer, too.
+ // We prefer downloading blocks from outbound peers to avoid
+ // putting undue load on (say) some home user who is just making
+ // outbound connections to the network, but if our only source of
+ // the latest blocks is from an inbound peer, we have to be sure to
+ // eventually download it (and not just wait indefinitely for an
+ // outbound peer to have it).
+ if (m_num_preferred_download_peers == 0 || mapBlocksInFlight.empty()) {
+ sync_blocks_and_headers_from_peer = true;
+ }
+ }
+
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) || m_chainman.m_best_header->GetBlockTime() > GetAdjustedTime() - 24 * 60 * 60) {
- state.fSyncStarted = true;
- state.m_headers_sync_timeout = current_time + HEADERS_DOWNLOAD_TIMEOUT_BASE +
- (
- // Convert HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER to microseconds before scaling
- // to maintain precision
- std::chrono::microseconds{HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER} *
- (GetAdjustedTime() - m_chainman.m_best_header->GetBlockTime()) / consensusParams.nPowTargetSpacing
- );
- nSyncStarted++;
+ if ((nSyncStarted == 0 && sync_blocks_and_headers_from_peer) || m_chainman.m_best_header->GetBlockTime() > GetAdjustedTime() - 24 * 60 * 60) {
const CBlockIndex* pindexStart = m_chainman.m_best_header;
/* If possible, start at the block preceding the currently
best known header. This ensures that we always get a
@@ -4740,8 +4868,19 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
got back an empty response. */
if (pindexStart->pprev)
pindexStart = pindexStart->pprev;
- LogPrint(BCLog::NET, "initial getheaders (%d) to peer=%d (startheight:%d)\n", pindexStart->nHeight, pto->GetId(), peer->m_starting_height);
- m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETHEADERS, m_chainman.ActiveChain().GetLocator(pindexStart), uint256()));
+ if (MaybeSendGetHeaders(*pto, m_chainman.ActiveChain().GetLocator(pindexStart), *peer)) {
+ LogPrint(BCLog::NET, "initial getheaders (%d) to peer=%d (startheight:%d)\n", pindexStart->nHeight, pto->GetId(), peer->m_starting_height);
+
+ state.fSyncStarted = true;
+ state.m_headers_sync_timeout = current_time + HEADERS_DOWNLOAD_TIMEOUT_BASE +
+ (
+ // Convert HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER to microseconds before scaling
+ // to maintain precision
+ std::chrono::microseconds{HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER} *
+ (GetAdjustedTime() - m_chainman.m_best_header->GetBlockTime()) / consensusParams.nPowTargetSpacing
+ );
+ nSyncStarted++;
+ }
}
}
@@ -4749,7 +4888,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
// Try sending block announcements via headers
//
{
- // If we have less than MAX_BLOCKS_TO_ANNOUNCE in our
+ // If we have no more than MAX_BLOCKS_TO_ANNOUNCE in our
// list of block hashes we're relaying, and our peer wants
// headers announcements, then find the first header
// not yet known to our peer but would connect, and send.
@@ -5087,13 +5226,13 @@ bool PeerManagerImpl::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<std::chrono::seconds>());
+ ConsiderEviction(*pto, *peer, GetTime<std::chrono::seconds>());
//
// Message: getdata (blocks)
//
std::vector<CInv> vGetData;
- if (!pto->fClient && ((fFetch && !pto->m_limited_node) || !m_chainman.ActiveChainstate().IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
+ if (!pto->fClient && ((sync_blocks_and_headers_from_peer && !pto->m_limited_node) || !m_chainman.ActiveChainstate().IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
std::vector<const CBlockIndex*> vToDownload;
NodeId staller = -1;
FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller);