Skip to content

Commit 573f631

Browse files
committed
Merge bitcoin/bitcoin#26326: net: don't lock cs_main while reading blocks in net processing
75d27fe net: reduce LOCK(cs_main) scope in ProcessGetBlockData (Andrew Toth) 613a45c net: reduce LOCK(cs_main) scope in GETBLOCKTXN (Andrew Toth) Pull request description: Inspired by bitcoin/bitcoin#11913 and bitcoin/bitcoin#26308. `cs_main` doesn't need to be locked while reading blocks. This removes the locks in `net_processing`. ACKs for top commit: sr-gi: ACK [75d27fe](bitcoin/bitcoin@75d27fe) achow101: ACK 75d27fe furszy: ACK 75d27fe with a non-blocking nit. mzumsande: Code Review ACK 75d27fe TheCharlatan: ACK 75d27fe Tree-SHA512: 79b85f748f68ecfb2f2afd3267857dd41b8e76dd482c9c922037399dcbce7b1e5d4c708a4f5fd17c3fb6699b0d88f26a17cc1d92db115dd43c8d4392ae27cec4
2 parents 4ff4276 + 75d27fe commit 573f631

File tree

1 file changed

+73
-43
lines changed

1 file changed

+73
-43
lines changed

src/net_processing.cpp

Lines changed: 73 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ static const unsigned int MAX_HEADERS_RESULTS = 2000;
118118
static const int MAX_CMPCTBLOCK_DEPTH = 5;
119119
/** Maximum depth of blocks we're willing to respond to GETBLOCKTXN requests for. */
120120
static const int MAX_BLOCKTXN_DEPTH = 10;
121+
static_assert(MAX_BLOCKTXN_DEPTH <= MIN_BLOCKS_TO_KEEP, "MAX_BLOCKTXN_DEPTH too high");
121122
/** Size of the "block download window": how far ahead of our current height do we fetch?
122123
* Larger windows tolerate larger download speed differences between peer, but increase the potential
123124
* degree of disordering of blocks on disk (which make reindexing and pruning harder). We'll probably
@@ -2420,55 +2421,77 @@ void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv&
24202421
}
24212422
}
24222423

2423-
LOCK(cs_main);
2424-
const CBlockIndex* pindex = m_chainman.m_blockman.LookupBlockIndex(inv.hash);
2425-
if (!pindex) {
2426-
return;
2427-
}
2428-
if (!BlockRequestAllowed(pindex)) {
2429-
LogPrint(BCLog::NET, "%s: ignoring request from peer=%i for old block that isn't in the main chain\n", __func__, pfrom.GetId());
2430-
return;
2431-
}
2432-
// disconnect node in case we have reached the outbound limit for serving historical blocks
2433-
if (m_connman.OutboundTargetReached(true) &&
2434-
(((m_chainman.m_best_header != nullptr) && (m_chainman.m_best_header->GetBlockTime() - pindex->GetBlockTime() > HISTORICAL_BLOCK_AGE)) || inv.IsMsgFilteredBlk()) &&
2435-
!pfrom.HasPermission(NetPermissionFlags::Download) // nodes with the download permission may exceed target
2436-
) {
2437-
LogPrint(BCLog::NET, "historical block serving limit reached, disconnect peer=%d\n", pfrom.GetId());
2438-
pfrom.fDisconnect = true;
2439-
return;
2440-
}
2441-
// Avoid leaking prune-height by never sending blocks below the NODE_NETWORK_LIMITED threshold
2442-
if (!pfrom.HasPermission(NetPermissionFlags::NoBan) && (
2443-
(((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 */) )
2444-
)) {
2445-
LogPrint(BCLog::NET, "Ignore block request below NODE_NETWORK_LIMITED threshold, disconnect peer=%d\n", pfrom.GetId());
2446-
//disconnect node and prevent it from stalling (would otherwise wait for the missing block)
2447-
pfrom.fDisconnect = true;
2448-
return;
2449-
}
2450-
// Pruned nodes may have deleted the block, so check whether
2451-
// it's available before trying to send.
2452-
if (!(pindex->nStatus & BLOCK_HAVE_DATA)) {
2453-
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();
24542464
}
2465+
24552466
std::shared_ptr<const CBlock> pblock;
24562467
if (a_recent_block && a_recent_block->GetHash() == pindex->GetBlockHash()) {
24572468
pblock = a_recent_block;
24582469
} else if (inv.IsMsgWitnessBlk()) {
24592470
// Fast-path: in this case it is possible to serve the block directly from disk,
24602471
// as the network format matches the format on disk
24612472
std::vector<uint8_t> block_data;
2462-
if (!m_chainman.m_blockman.ReadRawBlockFromDisk(block_data, pindex->GetBlockPos())) {
2463-
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;
24642481
}
24652482
MakeAndPushMessage(pfrom, NetMsgType::BLOCK, Span{block_data});
24662483
// Don't set pblock as we've sent the block
24672484
} else {
24682485
// Send block from disk
24692486
std::shared_ptr<CBlock> pblockRead = std::make_shared<CBlock>();
2470-
if (!m_chainman.m_blockman.ReadBlockFromDisk(*pblockRead, *pindex)) {
2471-
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;
24722495
}
24732496
pblock = pblockRead;
24742497
}
@@ -2506,7 +2529,7 @@ void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv&
25062529
// they won't have a useful mempool to match against a compact block,
25072530
// and we don't feel like constructing the object for them, so
25082531
// instead we respond with the full, non-compact block.
2509-
if (CanDirectFetch() && pindex->nHeight >= m_chainman.ActiveChain().Height() - MAX_CMPCTBLOCK_DEPTH) {
2532+
if (can_direct_fetch && pindex->nHeight >= tip->nHeight - MAX_CMPCTBLOCK_DEPTH) {
25102533
if (a_recent_compact_block && a_recent_compact_block->header.GetHash() == pindex->GetBlockHash()) {
25112534
MakeAndPushMessage(pfrom, NetMsgType::CMPCTBLOCK, *a_recent_compact_block);
25122535
} else {
@@ -2527,7 +2550,7 @@ void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv&
25272550
// and we want it right after the last block so they don't
25282551
// wait for other stuff first.
25292552
std::vector<CInv> vInv;
2530-
vInv.emplace_back(MSG_BLOCK, m_chainman.ActiveChain().Tip()->GetBlockHash());
2553+
vInv.emplace_back(MSG_BLOCK, tip->GetBlockHash());
25312554
MakeAndPushMessage(pfrom, NetMsgType::INV, vInv);
25322555
peer.m_continuation_block.SetNull();
25332556
}
@@ -4366,6 +4389,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
43664389
return;
43674390
}
43684391

4392+
FlatFilePos block_pos{};
43694393
{
43704394
LOCK(cs_main);
43714395

@@ -4376,15 +4400,21 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
43764400
}
43774401

43784402
if (pindex->nHeight >= m_chainman.ActiveChain().Height() - MAX_BLOCKTXN_DEPTH) {
4379-
CBlock block;
4380-
const bool ret{m_chainman.m_blockman.ReadBlockFromDisk(block, *pindex)};
4381-
assert(ret);
4382-
4383-
SendBlockTransactions(pfrom, *peer, block, req);
4384-
return;
4403+
block_pos = pindex->GetBlockPos();
43854404
}
43864405
}
43874406

4407+
if (!block_pos.IsNull()) {
4408+
CBlock block;
4409+
const bool ret{m_chainman.m_blockman.ReadBlockFromDisk(block, block_pos)};
4410+
// If height is above MAX_BLOCKTXN_DEPTH then this block cannot get
4411+
// pruned after we release cs_main above, so this read should never fail.
4412+
assert(ret);
4413+
4414+
SendBlockTransactions(pfrom, *peer, block, req);
4415+
return;
4416+
}
4417+
43884418
// If an older block is requested (should never happen in practice,
43894419
// but can happen in tests) send a block response instead of a
43904420
// blocktxn response. Sending a full block response instead of a

0 commit comments

Comments
 (0)