@@ -483,6 +483,9 @@ class PeerManagerImpl final : public PeerManager
483483 /* * Have we requested this block from a peer */
484484 bool IsBlockRequested (const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
485485
486+ /* * Have we requested this block from an outbound peer */
487+ bool IsBlockRequestedFromOutbound (const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
488+
486489 /* * Remove this block from our tracked requested blocks. Called if:
487490 * - the block has been received from a peer
488491 * - the request for the block has timed out
@@ -788,6 +791,17 @@ bool PeerManagerImpl::IsBlockRequested(const uint256& hash)
788791 return mapBlocksInFlight.count (hash);
789792}
790793
794+ bool PeerManagerImpl::IsBlockRequestedFromOutbound (const uint256& hash)
795+ {
796+ for (auto range = mapBlocksInFlight.equal_range (hash); range.first != range.second ; range.first ++) {
797+ auto & nodeblocksinflight = range.first ->second ;
798+ CNodeState& nodestate = *Assert (State (nodeblocksinflight.first ));
799+ if (!nodestate.m_is_inbound ) return true ;
800+ }
801+
802+ return false ;
803+ }
804+
791805void PeerManagerImpl::RemoveBlockRequest (const uint256& hash, std::optional<NodeId> from_peer)
792806{
793807 auto range = mapBlocksInFlight.equal_range (hash);
@@ -796,31 +810,30 @@ void PeerManagerImpl::RemoveBlockRequest(const uint256& hash, std::optional<Node
796810 return ;
797811 }
798812
799- // Currently we don't request more than one peer for same block
800- Assume (mapBlocksInFlight.count (hash) == 1 );
813+ // We should not have requested too many of this block
814+ Assume (mapBlocksInFlight.count (hash) <= MAX_CMPCTBLOCKS_INFLIGHT_PER_BLOCK );
801815
802816 while (range.first != range.second ) {
803- auto [node_id, list_it] = range.first ->second ;
817+ auto & nodeblocksinflight = range.first ->second ;
804818
805- if (from_peer && *from_peer != node_id ) {
819+ if (from_peer && *from_peer != nodeblocksinflight. first ) {
806820 range.first ++;
807821 continue ;
808822 }
809823
810- CNodeState *state = State (node_id);
811- assert (state != nullptr );
824+ CNodeState& state = *Assert (State (nodeblocksinflight.first ));
812825
813- if (state-> vBlocksInFlight .begin () == list_it ) {
826+ if (state. vBlocksInFlight .begin () == nodeblocksinflight. second ) {
814827 // First block on the queue was received, update the start download time for the next one
815- state-> m_downloading_since = std::max (state-> m_downloading_since , GetTime<std::chrono::microseconds>());
828+ state. m_downloading_since = std::max (state. m_downloading_since , GetTime<std::chrono::microseconds>());
816829 }
817- state-> vBlocksInFlight .erase (list_it );
830+ state. vBlocksInFlight .erase (nodeblocksinflight. second );
818831
819- if (state-> vBlocksInFlight .empty ()) {
832+ if (state. vBlocksInFlight .empty ()) {
820833 // Last validated block on the queue for this peer was received.
821834 m_peers_downloading_from--;
822835 }
823- state-> m_stalling_since = 0us;
836+ state. m_stalling_since = 0us;
824837
825838 range.first = mapBlocksInFlight.erase (range.first );
826839 }
@@ -833,6 +846,8 @@ bool PeerManagerImpl::BlockRequested(NodeId nodeid, const CBlockIndex& block, st
833846 CNodeState *state = State (nodeid);
834847 assert (state != nullptr );
835848
849+ Assume (mapBlocksInFlight.count (hash) <= MAX_CMPCTBLOCKS_INFLIGHT_PER_BLOCK );
850+
836851 // Short-circuit most stuff in case it is from the same node
837852 for (auto range = mapBlocksInFlight.equal_range (hash); range.first != range.second ; range.first ++) {
838853 if (range.first ->second .first == nodeid) {
@@ -843,8 +858,8 @@ bool PeerManagerImpl::BlockRequested(NodeId nodeid, const CBlockIndex& block, st
843858 }
844859 }
845860
846- // Make sure it's not listed somewhere already.
847- RemoveBlockRequest (hash, std:: nullopt );
861+ // Make sure it's not being fetched already from same peer .
862+ RemoveBlockRequest (hash, nodeid );
848863
849864 std::list<QueuedBlock>::iterator it = state->vBlocksInFlight .insert (state->vBlocksInFlight .end (),
850865 {&block, std::unique_ptr<PartiallyDownloadedBlock>(pit ? new PartiallyDownloadedBlock (&m_mempool) : nullptr )});
@@ -3464,20 +3479,23 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
34643479 return ;
34653480
34663481 auto range_flight = mapBlocksInFlight.equal_range (pindex->GetBlockHash ());
3467- bool fAlreadyInFlight = range_flight.first != range_flight.second ;
3468- bool in_flight_same_peer{false };
3482+ size_t already_in_flight = std::distance (range_flight.first , range_flight.second );
3483+ bool requested_block_from_this_peer{false };
3484+
3485+ // Multimap ensures ordering of outstanding requests. It's either empty or first in line.
3486+ bool first_in_flight = already_in_flight == 0 || (range_flight.first ->second .first == pfrom.GetId ());
34693487
34703488 while (range_flight.first != range_flight.second ) {
34713489 if (range_flight.first ->second .first == pfrom.GetId ()) {
3472- in_flight_same_peer = true ;
3490+ requested_block_from_this_peer = true ;
34733491 break ;
34743492 }
34753493 range_flight.first ++;
34763494 }
34773495
34783496 if (pindex->nChainWork <= m_chainman.ActiveChain ().Tip ()->nChainWork || // We know something better
34793497 pindex->nTx != 0 ) { // We had this block at some point, but pruned it
3480- if (in_flight_same_peer ) {
3498+ if (requested_block_from_this_peer ) {
34813499 // We requested this block for some reason, but our mempool will probably be useless
34823500 // so we just grab the block via normal getdata
34833501 std::vector<CInv> vInv (1 );
@@ -3488,7 +3506,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
34883506 }
34893507
34903508 // If we're not close to tip yet, give up and let parallel block fetch work its magic
3491- if (!fAlreadyInFlight && !CanDirectFetch ()) {
3509+ if (!already_in_flight && !CanDirectFetch ()) {
34923510 return ;
34933511 }
34943512
@@ -3501,8 +3519,8 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
35013519 // We want to be a bit conservative just to be extra careful about DoS
35023520 // possibilities in compact block processing...
35033521 if (pindex->nHeight <= m_chainman.ActiveChain ().Height () + 2 ) {
3504- if ((! fAlreadyInFlight && nodestate->vBlocksInFlight .size () < MAX_BLOCKS_IN_TRANSIT_PER_PEER ) ||
3505- in_flight_same_peer ) {
3522+ if ((already_in_flight < MAX_CMPCTBLOCKS_INFLIGHT_PER_BLOCK && nodestate->vBlocksInFlight .size () < MAX_BLOCKS_IN_TRANSIT_PER_PEER ) ||
3523+ requested_block_from_this_peer ) {
35063524 std::list<QueuedBlock>::iterator* queuedBlockIt = nullptr ;
35073525 if (!BlockRequested (pfrom.GetId (), *pindex, &queuedBlockIt)) {
35083526 if (!(*queuedBlockIt)->partialBlock )
@@ -3521,11 +3539,16 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
35213539 Misbehaving (pfrom.GetId (), 100 , " invalid compact block" );
35223540 return ;
35233541 } else if (status == READ_STATUS_FAILED ) {
3524- // Duplicate txindexes, the block is now in-flight, so just request it
3525- std::vector<CInv> vInv (1 );
3526- vInv[0 ] = CInv (MSG_BLOCK | GetFetchFlags (pfrom), cmpctblock.header .GetHash ());
3527- m_connman.PushMessage (&pfrom, msgMaker.Make (NetMsgType::GETDATA , vInv));
3528- return ;
3542+ if (first_in_flight) {
3543+ // Duplicate txindexes, the block is now in-flight, so just request it
3544+ std::vector<CInv> vInv (1 );
3545+ vInv[0 ] = CInv (MSG_BLOCK | GetFetchFlags (pfrom), cmpctblock.header .GetHash ());
3546+ m_connman.PushMessage (&pfrom, msgMaker.Make (NetMsgType::GETDATA , vInv));
3547+ return ;
3548+ } else {
3549+ // Give up for this peer and wait for other peer(s)
3550+ RemoveBlockRequest (pindex->GetBlockHash (), pfrom.GetId ());
3551+ }
35293552 }
35303553
35313554 BlockTransactionsRequest req;
@@ -3539,9 +3562,24 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
35393562 txn.blockhash = cmpctblock.header .GetHash ();
35403563 blockTxnMsg << txn;
35413564 fProcessBLOCKTXN = true ;
3542- } else {
3565+ } else if (first_in_flight) {
3566+ // We will try to round-trip any compact blocks we get on failure,
3567+ // as long as it's first...
35433568 req.blockhash = pindex->GetBlockHash ();
35443569 m_connman.PushMessage (&pfrom, msgMaker.Make (NetMsgType::GETBLOCKTXN , req));
3570+ } else if (pfrom.m_bip152_highbandwidth_to &&
3571+ (!pfrom.IsInboundConn () ||
3572+ IsBlockRequestedFromOutbound (cmpctblock.header .GetHash ()) ||
3573+ already_in_flight < MAX_CMPCTBLOCKS_INFLIGHT_PER_BLOCK - 1 )) {
3574+ // ... or it's a hb relay peer and:
3575+ // - peer is outbound, or
3576+ // - we already have an outbound attempt in flight(so we'll take what we can get), or
3577+ // - it's not the final parallel download slot (which we may reserve for first outbound)
3578+ req.blockhash = pindex->GetBlockHash ();
3579+ m_connman.PushMessage (&pfrom, msgMaker.Make (NetMsgType::GETBLOCKTXN , req));
3580+ } else {
3581+ // Give up for this peer and wait for other peer(s)
3582+ RemoveBlockRequest (pindex->GetBlockHash (), pfrom.GetId ());
35453583 }
35463584 } else {
35473585 // This block is either already in flight from a different
@@ -3562,7 +3600,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
35623600 }
35633601 }
35643602 } else {
3565- if (in_flight_same_peer ) {
3603+ if (requested_block_from_this_peer ) {
35663604 // We requested this block, but its far into the future, so our
35673605 // mempool will probably be useless - request the block normally
35683606 std::vector<CInv> vInv (1 );
@@ -3634,18 +3672,23 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
36343672 {
36353673 LOCK (cs_main);
36363674
3637- bool expected_blocktxn = false ;
36383675 auto range_flight = mapBlocksInFlight.equal_range (resp.blockhash );
3676+ size_t already_in_flight = std::distance (range_flight.first , range_flight.second );
3677+ bool requested_block_from_this_peer{false };
3678+
3679+ // Multimap ensures ordering of outstanding requests. It's either empty or first in line.
3680+ bool first_in_flight = already_in_flight == 0 || (range_flight.first ->second .first == pfrom.GetId ());
3681+
36393682 while (range_flight.first != range_flight.second ) {
36403683 auto [node_id, block_it] = range_flight.first ->second ;
36413684 if (node_id == pfrom.GetId () && block_it->partialBlock ) {
3642- expected_blocktxn = true ;
3685+ requested_block_from_this_peer = true ;
36433686 break ;
36443687 }
36453688 range_flight.first ++;
36463689 }
36473690
3648- if (!expected_blocktxn ) {
3691+ if (!requested_block_from_this_peer ) {
36493692 LogPrint (BCLog::NET , " Peer %d sent us block transactions for block we weren't expecting\n " , pfrom.GetId ());
36503693 return ;
36513694 }
@@ -3657,10 +3700,16 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
36573700 Misbehaving (pfrom.GetId (), 100 , " invalid compact block/non-matching block transactions" );
36583701 return ;
36593702 } else if (status == READ_STATUS_FAILED ) {
3660- // Might have collided, fall back to getdata now :(
3661- std::vector<CInv> invs;
3662- invs.push_back (CInv (MSG_BLOCK | GetFetchFlags (pfrom), resp.blockhash ));
3663- m_connman.PushMessage (&pfrom, msgMaker.Make (NetMsgType::GETDATA , invs));
3703+ if (first_in_flight) {
3704+ // Might have collided, fall back to getdata now :(
3705+ std::vector<CInv> invs;
3706+ invs.push_back (CInv (MSG_BLOCK | GetFetchFlags (pfrom), resp.blockhash ));
3707+ m_connman.PushMessage (&pfrom, msgMaker.Make (NetMsgType::GETDATA , invs));
3708+ } else {
3709+ RemoveBlockRequest (resp.blockhash , pfrom.GetId ());
3710+ LogPrint (BCLog::NET , " Peer %d sent us a compact block but it failed to reconstruct, waiting on first download to complete\n " , pfrom.GetId ());
3711+ return ;
3712+ }
36643713 } else {
36653714 // Block is either okay, or possibly we received
36663715 // READ_STATUS_CHECKBLOCK_FAILED.
0 commit comments