@@ -118,6 +118,7 @@ static const unsigned int MAX_HEADERS_RESULTS = 2000;
118
118
static const int MAX_CMPCTBLOCK_DEPTH = 5 ;
119
119
/* * Maximum depth of blocks we're willing to respond to GETBLOCKTXN requests for. */
120
120
static const int MAX_BLOCKTXN_DEPTH = 10 ;
121
+ static_assert (MAX_BLOCKTXN_DEPTH <= MIN_BLOCKS_TO_KEEP, " MAX_BLOCKTXN_DEPTH too high" );
121
122
/* * Size of the "block download window": how far ahead of our current height do we fetch?
122
123
* Larger windows tolerate larger download speed differences between peer, but increase the potential
123
124
* 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&
2420
2421
}
2421
2422
}
2422
2423
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 ();
2454
2464
}
2465
+
2455
2466
std::shared_ptr<const CBlock> pblock;
2456
2467
if (a_recent_block && a_recent_block->GetHash () == pindex->GetBlockHash ()) {
2457
2468
pblock = a_recent_block;
2458
2469
} else if (inv.IsMsgWitnessBlk ()) {
2459
2470
// Fast-path: in this case it is possible to serve the block directly from disk,
2460
2471
// as the network format matches the format on disk
2461
2472
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 ;
2464
2481
}
2465
2482
MakeAndPushMessage (pfrom, NetMsgType::BLOCK, Span{block_data});
2466
2483
// Don't set pblock as we've sent the block
2467
2484
} else {
2468
2485
// Send block from disk
2469
2486
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 ;
2472
2495
}
2473
2496
pblock = pblockRead;
2474
2497
}
@@ -2506,7 +2529,7 @@ void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv&
2506
2529
// they won't have a useful mempool to match against a compact block,
2507
2530
// and we don't feel like constructing the object for them, so
2508
2531
// 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) {
2510
2533
if (a_recent_compact_block && a_recent_compact_block->header .GetHash () == pindex->GetBlockHash ()) {
2511
2534
MakeAndPushMessage (pfrom, NetMsgType::CMPCTBLOCK, *a_recent_compact_block);
2512
2535
} else {
@@ -2527,7 +2550,7 @@ void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv&
2527
2550
// and we want it right after the last block so they don't
2528
2551
// wait for other stuff first.
2529
2552
std::vector<CInv> vInv;
2530
- vInv.emplace_back (MSG_BLOCK, m_chainman. ActiveChain (). Tip () ->GetBlockHash ());
2553
+ vInv.emplace_back (MSG_BLOCK, tip ->GetBlockHash ());
2531
2554
MakeAndPushMessage (pfrom, NetMsgType::INV, vInv);
2532
2555
peer.m_continuation_block .SetNull ();
2533
2556
}
@@ -4366,6 +4389,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
4366
4389
return ;
4367
4390
}
4368
4391
4392
+ FlatFilePos block_pos{};
4369
4393
{
4370
4394
LOCK (cs_main);
4371
4395
@@ -4376,15 +4400,21 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
4376
4400
}
4377
4401
4378
4402
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 ();
4385
4404
}
4386
4405
}
4387
4406
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
+
4388
4418
// If an older block is requested (should never happen in practice,
4389
4419
// but can happen in tests) send a block response instead of a
4390
4420
// blocktxn response. Sending a full block response instead of a
0 commit comments