@@ -918,6 +918,10 @@ class PeerManagerImpl final : public PeerManager
918
918
/* * Process a new block. Perform any post-processing housekeeping */
919
919
void ProcessBlock (CNode& node, const std::shared_ptr<const CBlock>& block, bool force_processing, bool min_pow_checked);
920
920
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
+
921
925
/* *
922
926
* When a peer sends us a valid block, instruct it to announce blocks to us
923
927
* 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
3204
3208
}
3205
3209
}
3206
3210
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
+
3207
3298
void PeerManagerImpl::ProcessMessage (CNode& pfrom, const std::string& msg_type, CDataStream& vRecv,
3208
3299
const std::chrono::microseconds time_received,
3209
3300
const std::atomic<bool >& interruptMsgProc)
@@ -4276,12 +4367,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
4276
4367
blockhash.ToString (), pfrom.GetId ());
4277
4368
}
4278
4369
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).
4283
4370
bool fProcessBLOCKTXN = false ;
4284
- CDataStream blockTxnMsg (SER_NETWORK, PROTOCOL_VERSION);
4285
4371
4286
4372
// If we end up treating this as a plain headers message, call that as well
4287
4373
// without cs_main.
@@ -4382,10 +4468,6 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
4382
4468
req.indexes .push_back (i);
4383
4469
}
4384
4470
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;
4389
4471
fProcessBLOCKTXN = true ;
4390
4472
} else if (first_in_flight) {
4391
4473
// 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,
4440
4522
} // cs_main
4441
4523
4442
4524
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);
4444
4528
}
4445
4529
4446
4530
if (fRevertToHeaderProcessing ) {
@@ -4492,88 +4576,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
4492
4576
BlockTransactions resp;
4493
4577
vRecv >> resp;
4494
4578
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);
4577
4580
}
4578
4581
4579
4582
if (msg_type == NetMsgType::HEADERS)
0 commit comments