Skip to content

Commit 75d27fe

Browse files
committed
net: reduce LOCK(cs_main) scope in ProcessGetBlockData
This also changes behavior if ReadBlockFromDisk or ReadRawBlockFromDisk fails. Previously, the node would crash due to an assert. This has been replaced with logging the error, disconnecting the peer, and returning early.
1 parent 613a45c commit 75d27fe

File tree

1 file changed

+59
-37
lines changed

1 file changed

+59
-37
lines changed

src/net_processing.cpp

Lines changed: 59 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2421,55 +2421,77 @@ void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv&
24212421
}
24222422
}
24232423

2424-
LOCK(cs_main);
2425-
const CBlockIndex* pindex = m_chainman.m_blockman.LookupBlockIndex(inv.hash);
2426-
if (!pindex) {
2427-
return;
2428-
}
2429-
if (!BlockRequestAllowed(pindex)) {
2430-
LogPrint(BCLog::NET, "%s: ignoring request from peer=%i for old block that isn't in the main chain\n", __func__, pfrom.GetId());
2431-
return;
2432-
}
2433-
// disconnect node in case we have reached the outbound limit for serving historical blocks
2434-
if (m_connman.OutboundTargetReached(true) &&
2435-
(((m_chainman.m_best_header != nullptr) && (m_chainman.m_best_header->GetBlockTime() - pindex->GetBlockTime() > HISTORICAL_BLOCK_AGE)) || inv.IsMsgFilteredBlk()) &&
2436-
!pfrom.HasPermission(NetPermissionFlags::Download) // nodes with the download permission may exceed target
2437-
) {
2438-
LogPrint(BCLog::NET, "historical block serving limit reached, disconnect peer=%d\n", pfrom.GetId());
2439-
pfrom.fDisconnect = true;
2440-
return;
2441-
}
2442-
// Avoid leaking prune-height by never sending blocks below the NODE_NETWORK_LIMITED threshold
2443-
if (!pfrom.HasPermission(NetPermissionFlags::NoBan) && (
2444-
(((peer.m_our_services & NODE_NETWORK_LIMITED) == NODE_NETWORK_LIMITED) && ((peer.m_our_services & NODE_NETWORK) != NODE_NETWORK) && (m_chainman.ActiveChain().Tip()->nHeight - pindex->nHeight > (int)NODE_NETWORK_LIMITED_MIN_BLOCKS + 2 /* add two blocks buffer extension for possible races */) )
2445-
)) {
2446-
LogPrint(BCLog::NET, "Ignore block request below NODE_NETWORK_LIMITED threshold, disconnect peer=%d\n", pfrom.GetId());
2447-
//disconnect node and prevent it from stalling (would otherwise wait for the missing block)
2448-
pfrom.fDisconnect = true;
2449-
return;
2450-
}
2451-
// Pruned nodes may have deleted the block, so check whether
2452-
// it's available before trying to send.
2453-
if (!(pindex->nStatus & BLOCK_HAVE_DATA)) {
2454-
return;
2424+
const CBlockIndex* pindex{nullptr};
2425+
const CBlockIndex* tip{nullptr};
2426+
bool can_direct_fetch{false};
2427+
FlatFilePos block_pos{};
2428+
{
2429+
LOCK(cs_main);
2430+
pindex = m_chainman.m_blockman.LookupBlockIndex(inv.hash);
2431+
if (!pindex) {
2432+
return;
2433+
}
2434+
if (!BlockRequestAllowed(pindex)) {
2435+
LogPrint(BCLog::NET, "%s: ignoring request from peer=%i for old block that isn't in the main chain\n", __func__, pfrom.GetId());
2436+
return;
2437+
}
2438+
// disconnect node in case we have reached the outbound limit for serving historical blocks
2439+
if (m_connman.OutboundTargetReached(true) &&
2440+
(((m_chainman.m_best_header != nullptr) && (m_chainman.m_best_header->GetBlockTime() - pindex->GetBlockTime() > HISTORICAL_BLOCK_AGE)) || inv.IsMsgFilteredBlk()) &&
2441+
!pfrom.HasPermission(NetPermissionFlags::Download) // nodes with the download permission may exceed target
2442+
) {
2443+
LogPrint(BCLog::NET, "historical block serving limit reached, disconnect peer=%d\n", pfrom.GetId());
2444+
pfrom.fDisconnect = true;
2445+
return;
2446+
}
2447+
tip = m_chainman.ActiveChain().Tip();
2448+
// Avoid leaking prune-height by never sending blocks below the NODE_NETWORK_LIMITED threshold
2449+
if (!pfrom.HasPermission(NetPermissionFlags::NoBan) && (
2450+
(((peer.m_our_services & NODE_NETWORK_LIMITED) == NODE_NETWORK_LIMITED) && ((peer.m_our_services & NODE_NETWORK) != NODE_NETWORK) && (tip->nHeight - pindex->nHeight > (int)NODE_NETWORK_LIMITED_MIN_BLOCKS + 2 /* add two blocks buffer extension for possible races */) )
2451+
)) {
2452+
LogPrint(BCLog::NET, "Ignore block request below NODE_NETWORK_LIMITED threshold, disconnect peer=%d\n", pfrom.GetId());
2453+
//disconnect node and prevent it from stalling (would otherwise wait for the missing block)
2454+
pfrom.fDisconnect = true;
2455+
return;
2456+
}
2457+
// Pruned nodes may have deleted the block, so check whether
2458+
// it's available before trying to send.
2459+
if (!(pindex->nStatus & BLOCK_HAVE_DATA)) {
2460+
return;
2461+
}
2462+
can_direct_fetch = CanDirectFetch();
2463+
block_pos = pindex->GetBlockPos();
24552464
}
2465+
24562466
std::shared_ptr<const CBlock> pblock;
24572467
if (a_recent_block && a_recent_block->GetHash() == pindex->GetBlockHash()) {
24582468
pblock = a_recent_block;
24592469
} else if (inv.IsMsgWitnessBlk()) {
24602470
// Fast-path: in this case it is possible to serve the block directly from disk,
24612471
// as the network format matches the format on disk
24622472
std::vector<uint8_t> block_data;
2463-
if (!m_chainman.m_blockman.ReadRawBlockFromDisk(block_data, pindex->GetBlockPos())) {
2464-
assert(!"cannot load block from disk");
2473+
if (!m_chainman.m_blockman.ReadRawBlockFromDisk(block_data, block_pos)) {
2474+
if (WITH_LOCK(m_chainman.GetMutex(), return m_chainman.m_blockman.IsBlockPruned(*pindex))) {
2475+
LogPrint(BCLog::NET, "Block was pruned before it could be read, disconnect peer=%s\n", pfrom.GetId());
2476+
} else {
2477+
LogError("Cannot load block from disk, disconnect peer=%d\n", pfrom.GetId());
2478+
}
2479+
pfrom.fDisconnect = true;
2480+
return;
24652481
}
24662482
MakeAndPushMessage(pfrom, NetMsgType::BLOCK, Span{block_data});
24672483
// Don't set pblock as we've sent the block
24682484
} else {
24692485
// Send block from disk
24702486
std::shared_ptr<CBlock> pblockRead = std::make_shared<CBlock>();
2471-
if (!m_chainman.m_blockman.ReadBlockFromDisk(*pblockRead, *pindex)) {
2472-
assert(!"cannot load block from disk");
2487+
if (!m_chainman.m_blockman.ReadBlockFromDisk(*pblockRead, block_pos)) {
2488+
if (WITH_LOCK(m_chainman.GetMutex(), return m_chainman.m_blockman.IsBlockPruned(*pindex))) {
2489+
LogPrint(BCLog::NET, "Block was pruned before it could be read, disconnect peer=%s\n", pfrom.GetId());
2490+
} else {
2491+
LogError("Cannot load block from disk, disconnect peer=%d\n", pfrom.GetId());
2492+
}
2493+
pfrom.fDisconnect = true;
2494+
return;
24732495
}
24742496
pblock = pblockRead;
24752497
}
@@ -2507,7 +2529,7 @@ void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv&
25072529
// they won't have a useful mempool to match against a compact block,
25082530
// and we don't feel like constructing the object for them, so
25092531
// instead we respond with the full, non-compact block.
2510-
if (CanDirectFetch() && pindex->nHeight >= m_chainman.ActiveChain().Height() - MAX_CMPCTBLOCK_DEPTH) {
2532+
if (can_direct_fetch && pindex->nHeight >= tip->nHeight - MAX_CMPCTBLOCK_DEPTH) {
25112533
if (a_recent_compact_block && a_recent_compact_block->header.GetHash() == pindex->GetBlockHash()) {
25122534
MakeAndPushMessage(pfrom, NetMsgType::CMPCTBLOCK, *a_recent_compact_block);
25132535
} else {
@@ -2528,7 +2550,7 @@ void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv&
25282550
// and we want it right after the last block so they don't
25292551
// wait for other stuff first.
25302552
std::vector<CInv> vInv;
2531-
vInv.emplace_back(MSG_BLOCK, m_chainman.ActiveChain().Tip()->GetBlockHash());
2553+
vInv.emplace_back(MSG_BLOCK, tip->GetBlockHash());
25322554
MakeAndPushMessage(pfrom, NetMsgType::INV, vInv);
25332555
peer.m_continuation_block.SetNull();
25342556
}

0 commit comments

Comments
 (0)