@@ -876,6 +876,9 @@ class PeerManagerImpl final : public PeerManager
876
876
/* * Have we requested this block from a peer */
877
877
bool IsBlockRequested (const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
878
878
879
+ /* * Have we requested this block from an outbound peer */
880
+ bool IsBlockRequestedFromOutbound (const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
881
+
879
882
/* * Remove this block from our tracked requested blocks. Called if:
880
883
* - the block has been received from a peer
881
884
* - the request for the block has timed out
@@ -1121,6 +1124,17 @@ bool PeerManagerImpl::IsBlockRequested(const uint256& hash)
1121
1124
return mapBlocksInFlight.count (hash);
1122
1125
}
1123
1126
1127
+ bool PeerManagerImpl::IsBlockRequestedFromOutbound (const uint256& hash)
1128
+ {
1129
+ for (auto range = mapBlocksInFlight.equal_range (hash); range.first != range.second ; range.first ++) {
1130
+ auto [nodeid, block_it] = range.first ->second ;
1131
+ CNodeState& nodestate = *Assert (State (nodeid));
1132
+ if (!nodestate.m_is_inbound ) return true ;
1133
+ }
1134
+
1135
+ return false ;
1136
+ }
1137
+
1124
1138
void PeerManagerImpl::RemoveBlockRequest (const uint256& hash, std::optional<NodeId> from_peer)
1125
1139
{
1126
1140
auto range = mapBlocksInFlight.equal_range (hash);
@@ -1129,8 +1143,8 @@ void PeerManagerImpl::RemoveBlockRequest(const uint256& hash, std::optional<Node
1129
1143
return ;
1130
1144
}
1131
1145
1132
- // Currently we don't request more than one peer for same block
1133
- Assume (mapBlocksInFlight.count (hash) == 1 );
1146
+ // We should not have requested too many of this block
1147
+ Assume (mapBlocksInFlight.count (hash) <= MAX_CMPCTBLOCKS_INFLIGHT_PER_BLOCK );
1134
1148
1135
1149
while (range.first != range.second ) {
1136
1150
auto [node_id, list_it] = range.first ->second ;
@@ -1140,20 +1154,19 @@ void PeerManagerImpl::RemoveBlockRequest(const uint256& hash, std::optional<Node
1140
1154
continue ;
1141
1155
}
1142
1156
1143
- CNodeState *state = State (node_id);
1144
- assert (state != nullptr );
1157
+ CNodeState& state = *Assert (State (node_id));
1145
1158
1146
- if (state-> vBlocksInFlight .begin () == list_it) {
1159
+ if (state. vBlocksInFlight .begin () == list_it) {
1147
1160
// First block on the queue was received, update the start download time for the next one
1148
- state-> m_downloading_since = std::max (state-> m_downloading_since , GetTime<std::chrono::microseconds>());
1161
+ state. m_downloading_since = std::max (state. m_downloading_since , GetTime<std::chrono::microseconds>());
1149
1162
}
1150
- state-> vBlocksInFlight .erase (list_it);
1163
+ state. vBlocksInFlight .erase (list_it);
1151
1164
1152
- if (state-> vBlocksInFlight .empty ()) {
1165
+ if (state. vBlocksInFlight .empty ()) {
1153
1166
// Last validated block on the queue for this peer was received.
1154
1167
m_peers_downloading_from--;
1155
1168
}
1156
- state-> m_stalling_since = 0us;
1169
+ state. m_stalling_since = 0us;
1157
1170
1158
1171
range.first = mapBlocksInFlight.erase (range.first );
1159
1172
}
@@ -1166,6 +1179,8 @@ bool PeerManagerImpl::BlockRequested(NodeId nodeid, const CBlockIndex& block, st
1166
1179
CNodeState *state = State (nodeid);
1167
1180
assert (state != nullptr );
1168
1181
1182
+ Assume (mapBlocksInFlight.count (hash) <= MAX_CMPCTBLOCKS_INFLIGHT_PER_BLOCK);
1183
+
1169
1184
// Short-circuit most stuff in case it is from the same node
1170
1185
for (auto range = mapBlocksInFlight.equal_range (hash); range.first != range.second ; range.first ++) {
1171
1186
if (range.first ->second .first == nodeid) {
@@ -1176,8 +1191,8 @@ bool PeerManagerImpl::BlockRequested(NodeId nodeid, const CBlockIndex& block, st
1176
1191
}
1177
1192
}
1178
1193
1179
- // Make sure it's not listed somewhere already.
1180
- RemoveBlockRequest (hash, std::nullopt );
1194
+ // Make sure it's not being fetched already from same peer .
1195
+ RemoveBlockRequest (hash, nodeid );
1181
1196
1182
1197
std::list<QueuedBlock>::iterator it = state->vBlocksInFlight .insert (state->vBlocksInFlight .end (),
1183
1198
{&block, std::unique_ptr<PartiallyDownloadedBlock>(pit ? new PartiallyDownloadedBlock (&m_mempool) : nullptr )});
@@ -1774,11 +1789,10 @@ std::optional<std::string> PeerManagerImpl::FetchBlock(NodeId peer_id, const CBl
1774
1789
1775
1790
LOCK (cs_main);
1776
1791
1777
- // Mark block as in-flight unless it already is (for this peer).
1778
- // If the peer does not send us a block, vBlocksInFlight remains non-empty,
1779
- // causing us to timeout and disconnect.
1780
- // If a block was already in-flight for a different peer, its BLOCKTXN
1781
- // response will be dropped.
1792
+ // Forget about all prior requests
1793
+ RemoveBlockRequest (block_index.GetBlockHash (), std::nullopt);
1794
+
1795
+ // Mark block as in-flight
1782
1796
if (!BlockRequested (peer_id, block_index)) return " Already requested from this peer" ;
1783
1797
1784
1798
// Construct message to request the block
@@ -4292,20 +4306,23 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
4292
4306
return ;
4293
4307
4294
4308
auto range_flight = mapBlocksInFlight.equal_range (pindex->GetBlockHash ());
4295
- bool fAlreadyInFlight = range_flight.first != range_flight.second ;
4296
- bool in_flight_same_peer{false };
4309
+ size_t already_in_flight = std::distance (range_flight.first , range_flight.second );
4310
+ bool requested_block_from_this_peer{false };
4311
+
4312
+ // Multimap ensures ordering of outstanding requests. It's either empty or first in line.
4313
+ bool first_in_flight = already_in_flight == 0 || (range_flight.first ->second .first == pfrom.GetId ());
4297
4314
4298
4315
while (range_flight.first != range_flight.second ) {
4299
4316
if (range_flight.first ->second .first == pfrom.GetId ()) {
4300
- in_flight_same_peer = true ;
4317
+ requested_block_from_this_peer = true ;
4301
4318
break ;
4302
4319
}
4303
4320
range_flight.first ++;
4304
4321
}
4305
4322
4306
4323
if (pindex->nChainWork <= m_chainman.ActiveChain ().Tip ()->nChainWork || // We know something better
4307
4324
pindex->nTx != 0 ) { // We had this block at some point, but pruned it
4308
- if (in_flight_same_peer ) {
4325
+ if (requested_block_from_this_peer ) {
4309
4326
// We requested this block for some reason, but our mempool will probably be useless
4310
4327
// so we just grab the block via normal getdata
4311
4328
std::vector<CInv> vInv (1 );
@@ -4316,15 +4333,15 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
4316
4333
}
4317
4334
4318
4335
// If we're not close to tip yet, give up and let parallel block fetch work its magic
4319
- if (!fAlreadyInFlight && !CanDirectFetch ()) {
4336
+ if (!already_in_flight && !CanDirectFetch ()) {
4320
4337
return ;
4321
4338
}
4322
4339
4323
4340
// We want to be a bit conservative just to be extra careful about DoS
4324
4341
// possibilities in compact block processing...
4325
4342
if (pindex->nHeight <= m_chainman.ActiveChain ().Height () + 2 ) {
4326
- if ((! fAlreadyInFlight && nodestate->vBlocksInFlight .size () < MAX_BLOCKS_IN_TRANSIT_PER_PEER) ||
4327
- in_flight_same_peer ) {
4343
+ if ((already_in_flight < MAX_CMPCTBLOCKS_INFLIGHT_PER_BLOCK && nodestate->vBlocksInFlight .size () < MAX_BLOCKS_IN_TRANSIT_PER_PEER) ||
4344
+ requested_block_from_this_peer ) {
4328
4345
std::list<QueuedBlock>::iterator* queuedBlockIt = nullptr ;
4329
4346
if (!BlockRequested (pfrom.GetId (), *pindex, &queuedBlockIt)) {
4330
4347
if (!(*queuedBlockIt)->partialBlock )
@@ -4343,11 +4360,16 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
4343
4360
Misbehaving (*peer, 100 , " invalid compact block" );
4344
4361
return ;
4345
4362
} else if (status == READ_STATUS_FAILED) {
4346
- // Duplicate txindexes, the block is now in-flight, so just request it
4347
- std::vector<CInv> vInv (1 );
4348
- vInv[0 ] = CInv (MSG_BLOCK | GetFetchFlags (*peer), blockhash);
4349
- m_connman.PushMessage (&pfrom, msgMaker.Make (NetMsgType::GETDATA, vInv));
4350
- return ;
4363
+ if (first_in_flight) {
4364
+ // Duplicate txindexes, the block is now in-flight, so just request it
4365
+ std::vector<CInv> vInv (1 );
4366
+ vInv[0 ] = CInv (MSG_BLOCK | GetFetchFlags (*peer), blockhash);
4367
+ m_connman.PushMessage (&pfrom, msgMaker.Make (NetMsgType::GETDATA, vInv));
4368
+ return ;
4369
+ } else {
4370
+ // Give up for this peer and wait for other peer(s)
4371
+ RemoveBlockRequest (pindex->GetBlockHash (), pfrom.GetId ());
4372
+ }
4351
4373
}
4352
4374
4353
4375
BlockTransactionsRequest req;
@@ -4361,9 +4383,24 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
4361
4383
txn.blockhash = blockhash;
4362
4384
blockTxnMsg << txn;
4363
4385
fProcessBLOCKTXN = true ;
4364
- } else {
4386
+ } else if (first_in_flight) {
4387
+ // We will try to round-trip any compact blocks we get on failure,
4388
+ // as long as it's first...
4389
+ req.blockhash = pindex->GetBlockHash ();
4390
+ m_connman.PushMessage (&pfrom, msgMaker.Make (NetMsgType::GETBLOCKTXN, req));
4391
+ } else if (pfrom.m_bip152_highbandwidth_to &&
4392
+ (!pfrom.IsInboundConn () ||
4393
+ IsBlockRequestedFromOutbound (blockhash) ||
4394
+ already_in_flight < MAX_CMPCTBLOCKS_INFLIGHT_PER_BLOCK - 1 )) {
4395
+ // ... or it's a hb relay peer and:
4396
+ // - peer is outbound, or
4397
+ // - we already have an outbound attempt in flight(so we'll take what we can get), or
4398
+ // - it's not the final parallel download slot (which we may reserve for first outbound)
4365
4399
req.blockhash = pindex->GetBlockHash ();
4366
4400
m_connman.PushMessage (&pfrom, msgMaker.Make (NetMsgType::GETBLOCKTXN, req));
4401
+ } else {
4402
+ // Give up for this peer and wait for other peer(s)
4403
+ RemoveBlockRequest (pindex->GetBlockHash (), pfrom.GetId ());
4367
4404
}
4368
4405
} else {
4369
4406
// This block is either already in flight from a different
@@ -4384,7 +4421,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
4384
4421
}
4385
4422
}
4386
4423
} else {
4387
- if (in_flight_same_peer ) {
4424
+ if (requested_block_from_this_peer ) {
4388
4425
// We requested this block, but its far into the future, so our
4389
4426
// mempool will probably be useless - request the block normally
4390
4427
std::vector<CInv> vInv (1 );
@@ -4456,18 +4493,23 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
4456
4493
{
4457
4494
LOCK (cs_main);
4458
4495
4459
- bool expected_blocktxn = false ;
4460
4496
auto range_flight = mapBlocksInFlight.equal_range (resp.blockhash );
4497
+ size_t already_in_flight = std::distance (range_flight.first , range_flight.second );
4498
+ bool requested_block_from_this_peer{false };
4499
+
4500
+ // Multimap ensures ordering of outstanding requests. It's either empty or first in line.
4501
+ bool first_in_flight = already_in_flight == 0 || (range_flight.first ->second .first == pfrom.GetId ());
4502
+
4461
4503
while (range_flight.first != range_flight.second ) {
4462
4504
auto [node_id, block_it] = range_flight.first ->second ;
4463
4505
if (node_id == pfrom.GetId () && block_it->partialBlock ) {
4464
- expected_blocktxn = true ;
4506
+ requested_block_from_this_peer = true ;
4465
4507
break ;
4466
4508
}
4467
4509
range_flight.first ++;
4468
4510
}
4469
4511
4470
- if (!expected_blocktxn ) {
4512
+ if (!requested_block_from_this_peer ) {
4471
4513
LogPrint (BCLog::NET, " Peer %d sent us block transactions for block we weren't expecting\n " , pfrom.GetId ());
4472
4514
return ;
4473
4515
}
@@ -4479,10 +4521,16 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
4479
4521
Misbehaving (*peer, 100 , " invalid compact block/non-matching block transactions" );
4480
4522
return ;
4481
4523
} else if (status == READ_STATUS_FAILED) {
4482
- // Might have collided, fall back to getdata now :(
4483
- std::vector<CInv> invs;
4484
- invs.push_back (CInv (MSG_BLOCK | GetFetchFlags (*peer), resp.blockhash ));
4485
- m_connman.PushMessage (&pfrom, msgMaker.Make (NetMsgType::GETDATA, invs));
4524
+ if (first_in_flight) {
4525
+ // Might have collided, fall back to getdata now :(
4526
+ std::vector<CInv> invs;
4527
+ invs.push_back (CInv (MSG_BLOCK | GetFetchFlags (*peer), resp.blockhash ));
4528
+ m_connman.PushMessage (&pfrom, msgMaker.Make (NetMsgType::GETDATA, invs));
4529
+ } else {
4530
+ RemoveBlockRequest (resp.blockhash , pfrom.GetId ());
4531
+ LogPrint (BCLog::NET, " Peer %d sent us a compact block but it failed to reconstruct, waiting on first download to complete\n " , pfrom.GetId ());
4532
+ return ;
4533
+ }
4486
4534
} else {
4487
4535
// Block is either okay, or possibly we received
4488
4536
// READ_STATUS_CHECKBLOCK_FAILED.
0 commit comments