@@ -876,6 +876,9 @@ class PeerManagerImpl final : public PeerManager
876876 /* * Have we requested this block from a peer */
877877 bool IsBlockRequested (const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
878878
879+ /* * Have we requested this block from an outbound peer */
880+ bool IsBlockRequestedFromOutbound (const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
881+
879882 /* * Remove this block from our tracked requested blocks. Called if:
880883 * - the block has been received from a peer
881884 * - the request for the block has timed out
@@ -1121,6 +1124,17 @@ bool PeerManagerImpl::IsBlockRequested(const uint256& hash)
11211124 return mapBlocksInFlight.count (hash);
11221125}
11231126
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+
11241138void PeerManagerImpl::RemoveBlockRequest (const uint256& hash, std::optional<NodeId> from_peer)
11251139{
11261140 auto range = mapBlocksInFlight.equal_range (hash);
@@ -1129,8 +1143,8 @@ void PeerManagerImpl::RemoveBlockRequest(const uint256& hash, std::optional<Node
11291143 return ;
11301144 }
11311145
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 );
11341148
11351149 while (range.first != range.second ) {
11361150 auto [node_id, list_it] = range.first ->second ;
@@ -1140,20 +1154,19 @@ void PeerManagerImpl::RemoveBlockRequest(const uint256& hash, std::optional<Node
11401154 continue ;
11411155 }
11421156
1143- CNodeState *state = State (node_id);
1144- assert (state != nullptr );
1157+ CNodeState& state = *Assert (State (node_id));
11451158
1146- if (state-> vBlocksInFlight .begin () == list_it) {
1159+ if (state. vBlocksInFlight .begin () == list_it) {
11471160 // 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>());
11491162 }
1150- state-> vBlocksInFlight .erase (list_it);
1163+ state. vBlocksInFlight .erase (list_it);
11511164
1152- if (state-> vBlocksInFlight .empty ()) {
1165+ if (state. vBlocksInFlight .empty ()) {
11531166 // Last validated block on the queue for this peer was received.
11541167 m_peers_downloading_from--;
11551168 }
1156- state-> m_stalling_since = 0us;
1169+ state. m_stalling_since = 0us;
11571170
11581171 range.first = mapBlocksInFlight.erase (range.first );
11591172 }
@@ -1166,6 +1179,8 @@ bool PeerManagerImpl::BlockRequested(NodeId nodeid, const CBlockIndex& block, st
11661179 CNodeState *state = State (nodeid);
11671180 assert (state != nullptr );
11681181
1182+ Assume (mapBlocksInFlight.count (hash) <= MAX_CMPCTBLOCKS_INFLIGHT_PER_BLOCK);
1183+
11691184 // Short-circuit most stuff in case it is from the same node
11701185 for (auto range = mapBlocksInFlight.equal_range (hash); range.first != range.second ; range.first ++) {
11711186 if (range.first ->second .first == nodeid) {
@@ -1176,8 +1191,8 @@ bool PeerManagerImpl::BlockRequested(NodeId nodeid, const CBlockIndex& block, st
11761191 }
11771192 }
11781193
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 );
11811196
11821197 std::list<QueuedBlock>::iterator it = state->vBlocksInFlight .insert (state->vBlocksInFlight .end (),
11831198 {&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
17741789
17751790 LOCK (cs_main);
17761791
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
17821796 if (!BlockRequested (peer_id, block_index)) return " Already requested from this peer" ;
17831797
17841798 // Construct message to request the block
@@ -4292,20 +4306,23 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
42924306 return ;
42934307
42944308 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 ());
42974314
42984315 while (range_flight.first != range_flight.second ) {
42994316 if (range_flight.first ->second .first == pfrom.GetId ()) {
4300- in_flight_same_peer = true ;
4317+ requested_block_from_this_peer = true ;
43014318 break ;
43024319 }
43034320 range_flight.first ++;
43044321 }
43054322
43064323 if (pindex->nChainWork <= m_chainman.ActiveChain ().Tip ()->nChainWork || // We know something better
43074324 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 ) {
43094326 // We requested this block for some reason, but our mempool will probably be useless
43104327 // so we just grab the block via normal getdata
43114328 std::vector<CInv> vInv (1 );
@@ -4316,15 +4333,15 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
43164333 }
43174334
43184335 // 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 ()) {
43204337 return ;
43214338 }
43224339
43234340 // We want to be a bit conservative just to be extra careful about DoS
43244341 // possibilities in compact block processing...
43254342 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 ) {
43284345 std::list<QueuedBlock>::iterator* queuedBlockIt = nullptr ;
43294346 if (!BlockRequested (pfrom.GetId (), *pindex, &queuedBlockIt)) {
43304347 if (!(*queuedBlockIt)->partialBlock )
@@ -4343,11 +4360,16 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
43434360 Misbehaving (*peer, 100 , " invalid compact block" );
43444361 return ;
43454362 } 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+ }
43514373 }
43524374
43534375 BlockTransactionsRequest req;
@@ -4361,9 +4383,24 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
43614383 txn.blockhash = blockhash;
43624384 blockTxnMsg << txn;
43634385 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)
43654399 req.blockhash = pindex->GetBlockHash ();
43664400 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 ());
43674404 }
43684405 } else {
43694406 // This block is either already in flight from a different
@@ -4384,7 +4421,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
43844421 }
43854422 }
43864423 } else {
4387- if (in_flight_same_peer ) {
4424+ if (requested_block_from_this_peer ) {
43884425 // We requested this block, but its far into the future, so our
43894426 // mempool will probably be useless - request the block normally
43904427 std::vector<CInv> vInv (1 );
@@ -4456,18 +4493,23 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
44564493 {
44574494 LOCK (cs_main);
44584495
4459- bool expected_blocktxn = false ;
44604496 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+
44614503 while (range_flight.first != range_flight.second ) {
44624504 auto [node_id, block_it] = range_flight.first ->second ;
44634505 if (node_id == pfrom.GetId () && block_it->partialBlock ) {
4464- expected_blocktxn = true ;
4506+ requested_block_from_this_peer = true ;
44654507 break ;
44664508 }
44674509 range_flight.first ++;
44684510 }
44694511
4470- if (!expected_blocktxn ) {
4512+ if (!requested_block_from_this_peer ) {
44714513 LogPrint (BCLog::NET, " Peer %d sent us block transactions for block we weren't expecting\n " , pfrom.GetId ());
44724514 return ;
44734515 }
@@ -4479,10 +4521,16 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
44794521 Misbehaving (*peer, 100 , " invalid compact block/non-matching block transactions" );
44804522 return ;
44814523 } 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+ }
44864534 } else {
44874535 // Block is either okay, or possibly we received
44884536 // READ_STATUS_CHECKBLOCK_FAILED.
0 commit comments