merge bitcoin#22141: net processing: Remove hash and fValidatedHeaders from QueuedBlock

This commit is contained in:
Kittywhiskers Van Gogh 2024-09-08 08:58:16 +00:00
parent c0e6792e27
commit 68657efc03
No known key found for this signature in database
GPG Key ID: 30CD0C065E5C4AAD

View File

@ -193,10 +193,10 @@ static constexpr uint64_t CMPCTBLOCKS_VERSION{1};
namespace { namespace {
/** Blocks that are in flight, and that are in the queue to be downloaded. */ /** Blocks that are in flight, and that are in the queue to be downloaded. */
struct QueuedBlock { struct QueuedBlock {
uint256 hash; /** BlockIndex. We must have this since we only request blocks when we've already validated the header. */
const CBlockIndex* pindex; //!< Optional. const CBlockIndex* pindex;
bool fValidatedHeaders; //!< Whether this block has validated headers at the time of request. /** Optional, used for CMPCTBLOCK downloads */
std::unique_ptr<PartiallyDownloadedBlock> partialBlock; //!< Optional, used for CMPCTBLOCK downloads std::unique_ptr<PartiallyDownloadedBlock> partialBlock;
}; };
/** /**
@ -398,7 +398,6 @@ struct CNodeState {
//! When the first entry in vBlocksInFlight started downloading. Don't care when vBlocksInFlight is empty. //! When the first entry in vBlocksInFlight started downloading. Don't care when vBlocksInFlight is empty.
std::chrono::microseconds m_downloading_since{0us}; std::chrono::microseconds m_downloading_since{0us};
int nBlocksInFlight{0}; int nBlocksInFlight{0};
int nBlocksInFlightValidHeaders{0};
//! Whether we consider this a preferred download peer. //! Whether we consider this a preferred download peer.
bool fPreferredDownload{false}; bool fPreferredDownload{false};
//! Whether this peer wants invs or headers (when possible) for block announcements. //! Whether this peer wants invs or headers (when possible) for block announcements.
@ -898,16 +897,20 @@ private:
/** Height of the highest block announced using BIP 152 high-bandwidth mode. */ /** Height of the highest block announced using BIP 152 high-bandwidth mode. */
int m_highest_fast_announce GUARDED_BY(::cs_main){0}; int m_highest_fast_announce GUARDED_BY(::cs_main){0};
/* Returns a bool indicating whether we requested this block. /** Have we requested this block from a peer */
* Also used if a block was /not/ received and timed out or started with another peer bool IsBlockRequested(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Remove this block from our tracked requested blocks. Called if:
* - the block has been recieved from a peer
* - the request for the block has timed out
*/ */
bool MarkBlockAsReceived(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main); void RemoveBlockRequest(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/* Mark a block as in flight /* Mark a block as in flight
* Returns false, still setting pit, if the block was already in flight from the same peer * Returns false, still setting pit, if the block was already in flight from the same peer
* pit will only be valid as long as the same cs_main lock is being held * pit will only be valid as long as the same cs_main lock is being held
*/ */
bool MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const CBlockIndex* pindex = nullptr, std::list<QueuedBlock>::iterator** pit = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_main); bool BlockRequested(NodeId nodeid, const CBlockIndex* pindex, std::list<QueuedBlock>::iterator** pit = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
bool TipMayBeStale() EXCLUSIVE_LOCKS_REQUIRED(cs_main); bool TipMayBeStale() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@ -946,7 +949,7 @@ private:
std::list<NodeId> lNodesAnnouncingHeaderAndIDs GUARDED_BY(cs_main); std::list<NodeId> lNodesAnnouncingHeaderAndIDs GUARDED_BY(cs_main);
/** Number of peers from which we're downloading blocks. */ /** Number of peers from which we're downloading blocks. */
int nPeersWithValidatedDownloads GUARDED_BY(cs_main) = 0; int m_peers_downloading_from GUARDED_BY(cs_main) = 0;
/** Storage for orphan information */ /** Storage for orphan information */
TxOrphanage m_orphanage; TxOrphanage m_orphanage;
@ -1074,32 +1077,43 @@ std::chrono::microseconds PeerManagerImpl::NextInvToInbounds(std::chrono::micros
return m_next_inv_to_inbounds; return m_next_inv_to_inbounds;
} }
bool PeerManagerImpl::MarkBlockAsReceived(const uint256& hash) bool PeerManagerImpl::IsBlockRequested(const uint256& hash)
{ {
std::map<uint256, std::pair<NodeId, std::list<QueuedBlock>::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash); return mapBlocksInFlight.find(hash) != mapBlocksInFlight.end();
if (itInFlight != mapBlocksInFlight.end()) {
CNodeState *state = State(itInFlight->second.first);
assert(state != nullptr);
state->nBlocksInFlightValidHeaders -= itInFlight->second.second->fValidatedHeaders;
if (state->nBlocksInFlightValidHeaders == 0 && itInFlight->second.second->fValidatedHeaders) {
// Last validated block on the queue was received.
nPeersWithValidatedDownloads--;
}
if (state->vBlocksInFlight.begin() == itInFlight->second.second) {
// First block on the queue was received, update the start download time for the next one
state->m_downloading_since = std::max(state->m_downloading_since, GetTime<std::chrono::microseconds>());
}
state->vBlocksInFlight.erase(itInFlight->second.second);
state->nBlocksInFlight--;
state->m_stalling_since = 0us;
mapBlocksInFlight.erase(itInFlight);
return true;
}
return false;
} }
bool PeerManagerImpl::MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const CBlockIndex *pindex, std::list<QueuedBlock>::iterator **pit) void PeerManagerImpl::RemoveBlockRequest(const uint256& hash)
{ {
auto it = mapBlocksInFlight.find(hash);
if (it == mapBlocksInFlight.end()) {
// Block was not requested
return;
}
auto [node_id, list_it] = it->second;
CNodeState *state = State(node_id);
assert(state != nullptr);
if (state->vBlocksInFlight.begin() == list_it) {
// First block on the queue was received, update the start download time for the next one
state->m_downloading_since = std::max(state->m_downloading_since, GetTime<std::chrono::microseconds>());
}
state->vBlocksInFlight.erase(list_it);
state->nBlocksInFlight--;
if (state->nBlocksInFlight == 0) {
// Last validated block on the queue was received.
m_peers_downloading_from--;
}
state->m_stalling_since = 0us;
mapBlocksInFlight.erase(it);
}
bool PeerManagerImpl::BlockRequested(NodeId nodeid, const CBlockIndex *pindex, std::list<QueuedBlock>::iterator **pit)
{
assert(pindex);
const uint256& hash{pindex->GetBlockHash()};
CNodeState *state = State(nodeid); CNodeState *state = State(nodeid);
assert(state != nullptr); assert(state != nullptr);
@ -1113,18 +1127,15 @@ bool PeerManagerImpl::MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, co
} }
// Make sure it's not listed somewhere already. // Make sure it's not listed somewhere already.
MarkBlockAsReceived(hash); RemoveBlockRequest(hash);
std::list<QueuedBlock>::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(), std::list<QueuedBlock>::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(),
{hash, pindex, pindex != nullptr, std::unique_ptr<PartiallyDownloadedBlock>(pit ? new PartiallyDownloadedBlock(&m_mempool) : nullptr)}); {pindex, std::unique_ptr<PartiallyDownloadedBlock>(pit ? new PartiallyDownloadedBlock(&m_mempool) : nullptr)});
state->nBlocksInFlight++; state->nBlocksInFlight++;
state->nBlocksInFlightValidHeaders += it->fValidatedHeaders;
if (state->nBlocksInFlight == 1) { if (state->nBlocksInFlight == 1) {
// We're starting a block download (batch) from this peer. // We're starting a block download (batch) from this peer.
state->m_downloading_since = GetTime<std::chrono::microseconds>(); state->m_downloading_since = GetTime<std::chrono::microseconds>();
} m_peers_downloading_from++;
if (state->nBlocksInFlightValidHeaders == 1 && pindex != nullptr) {
nPeersWithValidatedDownloads++;
} }
itInFlight = mapBlocksInFlight.insert(std::make_pair(hash, std::make_pair(nodeid, it))).first; itInFlight = mapBlocksInFlight.insert(std::make_pair(hash, std::make_pair(nodeid, it))).first;
if (pit) if (pit)
@ -1310,7 +1321,7 @@ void PeerManagerImpl::FindNextBlocksToDownload(const Peer& peer, unsigned int co
if (pindex->nStatus & BLOCK_HAVE_DATA || m_chainman.ActiveChain().Contains(pindex)) { if (pindex->nStatus & BLOCK_HAVE_DATA || m_chainman.ActiveChain().Contains(pindex)) {
if (pindex->HaveTxsDownloaded()) if (pindex->HaveTxsDownloaded())
state->pindexLastCommonBlock = pindex; state->pindexLastCommonBlock = pindex;
} else if (mapBlocksInFlight.count(pindex->GetBlockHash()) == 0) { } else if (!IsBlockRequested(pindex->GetBlockHash())) {
// The block is not already downloaded, and not yet in flight. // The block is not already downloaded, and not yet in flight.
if (pindex->nHeight > nWindowEnd) { if (pindex->nHeight > nWindowEnd) {
// We reached the end of the window. // We reached the end of the window.
@ -1565,12 +1576,12 @@ void PeerManagerImpl::FinalizeNode(const CNode& node) {
nSyncStarted--; nSyncStarted--;
for (const QueuedBlock& entry : state->vBlocksInFlight) { for (const QueuedBlock& entry : state->vBlocksInFlight) {
mapBlocksInFlight.erase(entry.hash); mapBlocksInFlight.erase(entry.pindex->GetBlockHash());
} }
WITH_LOCK(g_cs_orphans, m_orphanage.EraseForPeer(nodeid)); WITH_LOCK(g_cs_orphans, m_orphanage.EraseForPeer(nodeid));
m_num_preferred_download_peers -= state->fPreferredDownload; m_num_preferred_download_peers -= state->fPreferredDownload;
nPeersWithValidatedDownloads -= (state->nBlocksInFlightValidHeaders != 0); m_peers_downloading_from -= (state->nBlocksInFlight != 0);
assert(nPeersWithValidatedDownloads >= 0); assert(m_peers_downloading_from >= 0);
m_outbound_peers_with_protect_from_disconnect -= state->m_chain_sync.m_protect; m_outbound_peers_with_protect_from_disconnect -= state->m_chain_sync.m_protect;
assert(m_outbound_peers_with_protect_from_disconnect >= 0); assert(m_outbound_peers_with_protect_from_disconnect >= 0);
@ -1580,7 +1591,7 @@ void PeerManagerImpl::FinalizeNode(const CNode& node) {
// Do a consistency check after the last peer is removed. // Do a consistency check after the last peer is removed.
assert(mapBlocksInFlight.empty()); assert(mapBlocksInFlight.empty());
assert(m_num_preferred_download_peers == 0); assert(m_num_preferred_download_peers == 0);
assert(nPeersWithValidatedDownloads == 0); assert(m_peers_downloading_from == 0);
assert(m_outbound_peers_with_protect_from_disconnect == 0); assert(m_outbound_peers_with_protect_from_disconnect == 0);
} }
} // cs_main } // cs_main
@ -1811,10 +1822,10 @@ std::optional<std::string> PeerManagerImpl::FetchBlock(NodeId peer_id, const CBl
// Mark block as in-flight unless it already is (for this peer). // Mark block as in-flight unless it already is (for this peer).
// If a block was already in-flight for a different peer, its BLOCKTXN // If a block was already in-flight for a different peer, its BLOCKTXN
// response will be dropped. // response will be dropped.
const uint256& hash{block_index.GetBlockHash()}; if (!BlockRequested(peer_id, &block_index)) return "Already requested from this peer";
if (!MarkBlockAsInFlight(peer_id, hash, &block_index)) return "Already requested from this peer";
// Construct message to request the block // Construct message to request the block
const uint256& hash{block_index.GetBlockHash()};
std::vector<CInv> invs{CInv(MSG_BLOCK, hash)}; std::vector<CInv> invs{CInv(MSG_BLOCK, hash)};
// Send block request message to the peer // Send block request message to the peer
@ -2778,7 +2789,7 @@ void PeerManagerImpl::HeadersDirectFetchBlocks(CNode& pfrom, const Peer& peer, c
// Calculate all the blocks we'd need to switch to pindexLast, up to a limit. // 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) { while (pindexWalk && !m_chainman.ActiveChain().Contains(pindexWalk) && vToFetch.size() <= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
if (!(pindexWalk->nStatus & BLOCK_HAVE_DATA) && if (!(pindexWalk->nStatus & BLOCK_HAVE_DATA) &&
!mapBlocksInFlight.count(pindexWalk->GetBlockHash())) { !IsBlockRequested(pindexWalk->GetBlockHash())) {
// We don't have this block, and it's not yet in flight. // We don't have this block, and it's not yet in flight.
vToFetch.push_back(pindexWalk); vToFetch.push_back(pindexWalk);
} }
@ -2801,7 +2812,7 @@ void PeerManagerImpl::HeadersDirectFetchBlocks(CNode& pfrom, const Peer& peer, c
break; break;
} }
vGetData.push_back(CInv(MSG_BLOCK, pindex->GetBlockHash())); vGetData.push_back(CInv(MSG_BLOCK, pindex->GetBlockHash()));
MarkBlockAsInFlight(pfrom.GetId(), pindex->GetBlockHash(), pindex); BlockRequested(pfrom.GetId(), pindex);
LogPrint(BCLog::NET, "Requesting block %s from peer=%d\n", LogPrint(BCLog::NET, "Requesting block %s from peer=%d\n",
pindex->GetBlockHash().ToString(), pfrom.GetId()); pindex->GetBlockHash().ToString(), pfrom.GetId());
} }
@ -3762,7 +3773,7 @@ void PeerManagerImpl::ProcessMessage(
statsClient.inc(strprintf("message.received.inv_%s", inv.GetCommand()), 1.0f); statsClient.inc(strprintf("message.received.inv_%s", inv.GetCommand()), 1.0f);
UpdateBlockAvailability(pfrom.GetId(), inv.hash); UpdateBlockAvailability(pfrom.GetId(), inv.hash);
if (!fAlreadyHave && !fImporting && !fReindex && !mapBlocksInFlight.count(inv.hash)) { if (!fAlreadyHave && !fImporting && !fReindex && !IsBlockRequested(inv.hash)) {
// Headers-first is the primary method of announcement on // Headers-first is the primary method of announcement on
// the network. If a node fell back to sending blocks by // the network. If a node fell back to sending blocks by
// inv, it may be for a re-org, or because we haven't // inv, it may be for a re-org, or because we haven't
@ -4342,7 +4353,7 @@ void PeerManagerImpl::ProcessMessage(
if ((!fAlreadyInFlight && nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) || 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; std::list<QueuedBlock>::iterator *queuedBlockIt = nullptr;
if (!MarkBlockAsInFlight(pfrom.GetId(), pindex->GetBlockHash(), pindex, &queuedBlockIt)) { if (!BlockRequested(pfrom.GetId(), pindex, &queuedBlockIt)) {
if (!(*queuedBlockIt)->partialBlock) if (!(*queuedBlockIt)->partialBlock)
(*queuedBlockIt)->partialBlock.reset(new PartiallyDownloadedBlock(&m_mempool)); (*queuedBlockIt)->partialBlock.reset(new PartiallyDownloadedBlock(&m_mempool));
else { else {
@ -4355,7 +4366,7 @@ void PeerManagerImpl::ProcessMessage(
PartiallyDownloadedBlock& partialBlock = *(*queuedBlockIt)->partialBlock; PartiallyDownloadedBlock& partialBlock = *(*queuedBlockIt)->partialBlock;
ReadStatus status = partialBlock.InitData(cmpctblock, vExtraTxnForCompact); ReadStatus status = partialBlock.InitData(cmpctblock, vExtraTxnForCompact);
if (status == READ_STATUS_INVALID) { if (status == READ_STATUS_INVALID) {
MarkBlockAsReceived(pindex->GetBlockHash()); // Reset in-flight state in case Misbehaving does not result in a disconnect RemoveBlockRequest(pindex->GetBlockHash()); // Reset in-flight state in case Misbehaving does not result in a disconnect
Misbehaving(pfrom.GetId(), 100, "invalid compact block"); Misbehaving(pfrom.GetId(), 100, "invalid compact block");
return; return;
} else if (status == READ_STATUS_FAILED) { } else if (status == READ_STATUS_FAILED) {
@ -4449,7 +4460,7 @@ void PeerManagerImpl::ProcessMessage(
// process from some other peer. We do this after calling // process from some other peer. We do this after calling
// ProcessNewBlock so that a malleated cmpctblock announcement // ProcessNewBlock so that a malleated cmpctblock announcement
// can't be used to interfere with block relay. // can't be used to interfere with block relay.
MarkBlockAsReceived(pblock->GetHash()); RemoveBlockRequest(pblock->GetHash());
} }
} }
return; return;
@ -4481,7 +4492,7 @@ void PeerManagerImpl::ProcessMessage(
PartiallyDownloadedBlock& partialBlock = *it->second.second->partialBlock; PartiallyDownloadedBlock& partialBlock = *it->second.second->partialBlock;
ReadStatus status = partialBlock.FillBlock(*pblock, resp.txn); ReadStatus status = partialBlock.FillBlock(*pblock, resp.txn);
if (status == READ_STATUS_INVALID) { if (status == READ_STATUS_INVALID) {
MarkBlockAsReceived(resp.blockhash); // Reset in-flight state in case Misbehaving does not result in a disconnect 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(pfrom.GetId(), 100, "invalid compact block/non-matching block transactions");
return; return;
} else if (status == READ_STATUS_FAILED) { } else if (status == READ_STATUS_FAILED) {
@ -4507,7 +4518,7 @@ void PeerManagerImpl::ProcessMessage(
// though the block was successfully read, and rely on the // though the block was successfully read, and rely on the
// handling in ProcessNewBlock to ensure the block index is // handling in ProcessNewBlock to ensure the block index is
// updated, etc. // updated, etc.
MarkBlockAsReceived(resp.blockhash); // it is now an empty pointer RemoveBlockRequest(resp.blockhash); // it is now an empty pointer
fBlockRead = true; fBlockRead = true;
// mapBlockSource is used for potentially punishing peers and // mapBlockSource is used for potentially punishing peers and
// updating which peers send us compact blocks, so the race // updating which peers send us compact blocks, so the race
@ -4586,9 +4597,10 @@ void PeerManagerImpl::ProcessMessage(
const uint256 hash(pblock->GetHash()); const uint256 hash(pblock->GetHash());
{ {
LOCK(cs_main); LOCK(cs_main);
// Also always process if we requested the block explicitly, as we may // Always process the block if we requested it, since we may
// need it even though it is not a candidate for a new best tip. // need it even when it's not a candidate for a new best tip.
forceProcessing |= MarkBlockAsReceived(hash); forceProcessing = IsBlockRequested(hash);
RemoveBlockRequest(hash);
// mapBlockSource is only used for punishing peers and setting // mapBlockSource is only used for punishing peers and setting
// which peers send us compact blocks, so the race between here and // which peers send us compact blocks, so the race between here and
// cs_main in ProcessNewBlock is fine. // cs_main in ProcessNewBlock is fine.
@ -5844,9 +5856,9 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
// to unreasonably increase our timeout. // to unreasonably increase our timeout.
if (state.vBlocksInFlight.size() > 0) { if (state.vBlocksInFlight.size() > 0) {
QueuedBlock &queuedBlock = state.vBlocksInFlight.front(); QueuedBlock &queuedBlock = state.vBlocksInFlight.front();
int nOtherPeersWithValidatedDownloads = nPeersWithValidatedDownloads - (state.nBlocksInFlightValidHeaders > 0); int nOtherPeersWithValidatedDownloads = m_peers_downloading_from - 1;
if (current_time > state.m_downloading_since + std::chrono::seconds{consensusParams.nPowTargetSpacing} * (BLOCK_DOWNLOAD_TIMEOUT_BASE + BLOCK_DOWNLOAD_TIMEOUT_PER_PEER * nOtherPeersWithValidatedDownloads)) { if (current_time > state.m_downloading_since + std::chrono::seconds{consensusParams.nPowTargetSpacing} * (BLOCK_DOWNLOAD_TIMEOUT_BASE + BLOCK_DOWNLOAD_TIMEOUT_PER_PEER * nOtherPeersWithValidatedDownloads)) {
LogPrintf("Timeout downloading block %s from peer=%d, disconnecting\n", queuedBlock.hash.ToString(), pto->GetId()); LogPrintf("Timeout downloading block %s from peer=%d, disconnecting\n", queuedBlock.pindex->GetBlockHash().ToString(), pto->GetId());
pto->fDisconnect = true; pto->fDisconnect = true;
return true; return true;
} }
@ -5898,7 +5910,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
FindNextBlocksToDownload(*peer, MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller); FindNextBlocksToDownload(*peer, MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller);
for (const CBlockIndex *pindex : vToDownload) { for (const CBlockIndex *pindex : vToDownload) {
vGetData.push_back(CInv(MSG_BLOCK, pindex->GetBlockHash())); vGetData.push_back(CInv(MSG_BLOCK, pindex->GetBlockHash()));
MarkBlockAsInFlight(pto->GetId(), pindex->GetBlockHash(), pindex); BlockRequested(pto->GetId(), pindex);
LogPrint(BCLog::NET, "Requesting block %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(), LogPrint(BCLog::NET, "Requesting block %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(),
pindex->nHeight, pto->GetId()); pindex->nHeight, pto->GetId());
} }