@@ -918,6 +918,10 @@ class PeerManagerImpl final : public PeerManager
918918 /* * Process a new block. Perform any post-processing housekeeping */
919919 void ProcessBlock (CNode& node, const std::shared_ptr<const CBlock>& block, bool force_processing, bool min_pow_checked);
920920
921+ /* * Process compact block txns */
922+ void ProcessCompactBlockTxns (CNode& pfrom, Peer& peer, const BlockTransactions& block_transactions)
923+ EXCLUSIVE_LOCKS_REQUIRED(g_msgproc_mutex, !m_most_recent_block_mutex);
924+
921925 /* *
922926 * When a peer sends us a valid block, instruct it to announce blocks to us
923927 * using CMPCTBLOCK if possible by adding its nodeid to the end of
@@ -3204,6 +3208,93 @@ void PeerManagerImpl::ProcessBlock(CNode& node, const std::shared_ptr<const CBlo
32043208 }
32053209}
32063210
3211+ void PeerManagerImpl::ProcessCompactBlockTxns (CNode& pfrom, Peer& peer, const BlockTransactions& block_transactions)
3212+ {
3213+ std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>();
3214+ bool fBlockRead {false };
3215+ const CNetMsgMaker msgMaker (pfrom.GetCommonVersion ());
3216+ {
3217+ LOCK (cs_main);
3218+
3219+ auto range_flight = mapBlocksInFlight.equal_range (block_transactions.blockhash );
3220+ size_t already_in_flight = std::distance (range_flight.first , range_flight.second );
3221+ bool requested_block_from_this_peer{false };
3222+
3223+ // Multimap ensures ordering of outstanding requests. It's either empty or first in line.
3224+ bool first_in_flight = already_in_flight == 0 || (range_flight.first ->second .first == pfrom.GetId ());
3225+
3226+ while (range_flight.first != range_flight.second ) {
3227+ auto [node_id, block_it] = range_flight.first ->second ;
3228+ if (node_id == pfrom.GetId () && block_it->partialBlock ) {
3229+ requested_block_from_this_peer = true ;
3230+ break ;
3231+ }
3232+ range_flight.first ++;
3233+ }
3234+
3235+ if (!requested_block_from_this_peer) {
3236+ LogPrint (BCLog::NET, " Peer %d sent us block transactions for block we weren't expecting\n " , pfrom.GetId ());
3237+ return ;
3238+ }
3239+
3240+ PartiallyDownloadedBlock& partialBlock = *range_flight.first ->second .second ->partialBlock ;
3241+ ReadStatus status = partialBlock.FillBlock (*pblock, block_transactions.txn );
3242+ if (status == READ_STATUS_INVALID) {
3243+ RemoveBlockRequest (block_transactions.blockhash , pfrom.GetId ()); // Reset in-flight state in case Misbehaving does not result in a disconnect
3244+ Misbehaving (peer, 100 , " invalid compact block/non-matching block transactions" );
3245+ return ;
3246+ } else if (status == READ_STATUS_FAILED) {
3247+ if (first_in_flight) {
3248+ // Might have collided, fall back to getdata now :(
3249+ std::vector<CInv> invs;
3250+ invs.push_back (CInv (MSG_BLOCK | GetFetchFlags (peer), block_transactions.blockhash ));
3251+ m_connman.PushMessage (&pfrom, msgMaker.Make (NetMsgType::GETDATA, invs));
3252+ } else {
3253+ RemoveBlockRequest (block_transactions.blockhash , pfrom.GetId ());
3254+ LogPrint (BCLog::NET, " Peer %d sent us a compact block but it failed to reconstruct, waiting on first download to complete\n " , pfrom.GetId ());
3255+ return ;
3256+ }
3257+ } else {
3258+ // Block is either okay, or possibly we received
3259+ // READ_STATUS_CHECKBLOCK_FAILED.
3260+ // Note that CheckBlock can only fail for one of a few reasons:
3261+ // 1. bad-proof-of-work (impossible here, because we've already
3262+ // accepted the header)
3263+ // 2. merkleroot doesn't match the transactions given (already
3264+ // caught in FillBlock with READ_STATUS_FAILED, so
3265+ // impossible here)
3266+ // 3. the block is otherwise invalid (eg invalid coinbase,
3267+ // block is too big, too many legacy sigops, etc).
3268+ // So if CheckBlock failed, #3 is the only possibility.
3269+ // Under BIP 152, we don't discourage the peer unless proof of work is
3270+ // invalid (we don't require all the stateless checks to have
3271+ // been run). This is handled below, so just treat this as
3272+ // though the block was successfully read, and rely on the
3273+ // handling in ProcessNewBlock to ensure the block index is
3274+ // updated, etc.
3275+ RemoveBlockRequest (block_transactions.blockhash , pfrom.GetId ()); // it is now an empty pointer
3276+ fBlockRead = true ;
3277+ // mapBlockSource is used for potentially punishing peers and
3278+ // updating which peers send us compact blocks, so the race
3279+ // between here and cs_main in ProcessNewBlock is fine.
3280+ // BIP 152 permits peers to relay compact blocks after validating
3281+ // the header only; we should not punish peers if the block turns
3282+ // out to be invalid.
3283+ mapBlockSource.emplace (block_transactions.blockhash , std::make_pair (pfrom.GetId (), false ));
3284+ }
3285+ } // Don't hold cs_main when we call into ProcessNewBlock
3286+ if (fBlockRead ) {
3287+ // Since we requested this block (it was in mapBlocksInFlight), force it to be processed,
3288+ // even if it would not be a candidate for new tip (missing previous block, chain not long enough, etc)
3289+ // This bypasses some anti-DoS logic in AcceptBlock (eg to prevent
3290+ // disk-space attacks), but this should be safe due to the
3291+ // protections in the compact block handler -- see related comment
3292+ // in compact block optimistic reconstruction handling.
3293+ ProcessBlock (pfrom, pblock, /* force_processing=*/ true , /* min_pow_checked=*/ true );
3294+ }
3295+ return ;
3296+ }
3297+
32073298void PeerManagerImpl::ProcessMessage (CNode& pfrom, const std::string& msg_type, CDataStream& vRecv,
32083299 const std::chrono::microseconds time_received,
32093300 const std::atomic<bool >& interruptMsgProc)
@@ -4276,12 +4367,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
42764367 blockhash.ToString (), pfrom.GetId ());
42774368 }
42784369
4279- // When we succeed in decoding a block's txids from a cmpctblock
4280- // message we typically jump to the BLOCKTXN handling code, with a
4281- // dummy (empty) BLOCKTXN message, to re-use the logic there in
4282- // completing processing of the putative block (without cs_main).
42834370 bool fProcessBLOCKTXN = false ;
4284- CDataStream blockTxnMsg (SER_NETWORK, PROTOCOL_VERSION);
42854371
42864372 // If we end up treating this as a plain headers message, call that as well
42874373 // without cs_main.
@@ -4382,10 +4468,6 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
43824468 req.indexes .push_back (i);
43834469 }
43844470 if (req.indexes .empty ()) {
4385- // Dirty hack to jump to BLOCKTXN code (TODO: move message handling into their own functions)
4386- BlockTransactions txn;
4387- txn.blockhash = blockhash;
4388- blockTxnMsg << txn;
43894471 fProcessBLOCKTXN = true ;
43904472 } else if (first_in_flight) {
43914473 // We will try to round-trip any compact blocks we get on failure,
@@ -4440,7 +4522,9 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
44404522 } // cs_main
44414523
44424524 if (fProcessBLOCKTXN ) {
4443- return ProcessMessage (pfrom, NetMsgType::BLOCKTXN, blockTxnMsg, time_received, interruptMsgProc);
4525+ BlockTransactions txn;
4526+ txn.blockhash = blockhash;
4527+ return ProcessCompactBlockTxns (pfrom, *peer, txn);
44444528 }
44454529
44464530 if (fRevertToHeaderProcessing ) {
@@ -4492,88 +4576,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
44924576 BlockTransactions resp;
44934577 vRecv >> resp;
44944578
4495- std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>();
4496- bool fBlockRead = false ;
4497- {
4498- LOCK (cs_main);
4499-
4500- auto range_flight = mapBlocksInFlight.equal_range (resp.blockhash );
4501- size_t already_in_flight = std::distance (range_flight.first , range_flight.second );
4502- bool requested_block_from_this_peer{false };
4503-
4504- // Multimap ensures ordering of outstanding requests. It's either empty or first in line.
4505- bool first_in_flight = already_in_flight == 0 || (range_flight.first ->second .first == pfrom.GetId ());
4506-
4507- while (range_flight.first != range_flight.second ) {
4508- auto [node_id, block_it] = range_flight.first ->second ;
4509- if (node_id == pfrom.GetId () && block_it->partialBlock ) {
4510- requested_block_from_this_peer = true ;
4511- break ;
4512- }
4513- range_flight.first ++;
4514- }
4515-
4516- if (!requested_block_from_this_peer) {
4517- LogPrint (BCLog::NET, " Peer %d sent us block transactions for block we weren't expecting\n " , pfrom.GetId ());
4518- return ;
4519- }
4520-
4521- PartiallyDownloadedBlock& partialBlock = *range_flight.first ->second .second ->partialBlock ;
4522- ReadStatus status = partialBlock.FillBlock (*pblock, resp.txn );
4523- if (status == READ_STATUS_INVALID) {
4524- RemoveBlockRequest (resp.blockhash , pfrom.GetId ()); // Reset in-flight state in case Misbehaving does not result in a disconnect
4525- Misbehaving (*peer, 100 , " invalid compact block/non-matching block transactions" );
4526- return ;
4527- } else if (status == READ_STATUS_FAILED) {
4528- if (first_in_flight) {
4529- // Might have collided, fall back to getdata now :(
4530- std::vector<CInv> invs;
4531- invs.push_back (CInv (MSG_BLOCK | GetFetchFlags (*peer), resp.blockhash ));
4532- m_connman.PushMessage (&pfrom, msgMaker.Make (NetMsgType::GETDATA, invs));
4533- } else {
4534- RemoveBlockRequest (resp.blockhash , pfrom.GetId ());
4535- LogPrint (BCLog::NET, " Peer %d sent us a compact block but it failed to reconstruct, waiting on first download to complete\n " , pfrom.GetId ());
4536- return ;
4537- }
4538- } else {
4539- // Block is either okay, or possibly we received
4540- // READ_STATUS_CHECKBLOCK_FAILED.
4541- // Note that CheckBlock can only fail for one of a few reasons:
4542- // 1. bad-proof-of-work (impossible here, because we've already
4543- // accepted the header)
4544- // 2. merkleroot doesn't match the transactions given (already
4545- // caught in FillBlock with READ_STATUS_FAILED, so
4546- // impossible here)
4547- // 3. the block is otherwise invalid (eg invalid coinbase,
4548- // block is too big, too many legacy sigops, etc).
4549- // So if CheckBlock failed, #3 is the only possibility.
4550- // Under BIP 152, we don't discourage the peer unless proof of work is
4551- // invalid (we don't require all the stateless checks to have
4552- // been run). This is handled below, so just treat this as
4553- // though the block was successfully read, and rely on the
4554- // handling in ProcessNewBlock to ensure the block index is
4555- // updated, etc.
4556- RemoveBlockRequest (resp.blockhash , pfrom.GetId ()); // it is now an empty pointer
4557- fBlockRead = true ;
4558- // mapBlockSource is used for potentially punishing peers and
4559- // updating which peers send us compact blocks, so the race
4560- // between here and cs_main in ProcessNewBlock is fine.
4561- // BIP 152 permits peers to relay compact blocks after validating
4562- // the header only; we should not punish peers if the block turns
4563- // out to be invalid.
4564- mapBlockSource.emplace (resp.blockhash , std::make_pair (pfrom.GetId (), false ));
4565- }
4566- } // Don't hold cs_main when we call into ProcessNewBlock
4567- if (fBlockRead ) {
4568- // Since we requested this block (it was in mapBlocksInFlight), force it to be processed,
4569- // even if it would not be a candidate for new tip (missing previous block, chain not long enough, etc)
4570- // This bypasses some anti-DoS logic in AcceptBlock (eg to prevent
4571- // disk-space attacks), but this should be safe due to the
4572- // protections in the compact block handler -- see related comment
4573- // in compact block optimistic reconstruction handling.
4574- ProcessBlock (pfrom, pblock, /* force_processing=*/ true , /* min_pow_checked=*/ true );
4575- }
4576- return ;
4579+ return ProcessCompactBlockTxns (pfrom, *peer, resp);
45774580 }
45784581
45794582 if (msg_type == NetMsgType::HEADERS)
0 commit comments