7
7
8
8
#include " addrman.h"
9
9
#include " arith_uint256.h"
10
+ #include " blockencodings.h"
10
11
#include " chainparams.h"
11
12
#include " checkpoints.h"
12
13
#include " checkqueue.h"
@@ -197,8 +198,9 @@ namespace {
197
198
/* * Blocks that are in flight, and that are in the queue to be downloaded. Protected by cs_main. */
198
199
struct QueuedBlock {
199
200
uint256 hash;
200
- CBlockIndex* pindex; // !< Optional.
201
- bool fValidatedHeaders ; // !< Whether this block has validated headers at the time of request.
201
+ CBlockIndex* pindex; // !< Optional.
202
+ bool fValidatedHeaders ; // !< Whether this block has validated headers at the time of request.
203
+ std::unique_ptr<PartiallyDownloadedBlock> partialBlock; // !< Optional, used for CMPCTBLOCK downloads
202
204
};
203
205
map<uint256, pair<NodeId, list<QueuedBlock>::iterator> > mapBlocksInFlight;
204
206
@@ -364,6 +366,7 @@ void FinalizeNode(NodeId nodeid) {
364
366
365
367
// Requires cs_main.
366
368
// Returns a bool indicating whether we requested this block.
369
+ // Also used if a block was /not/ received and timed out or started with another peer
367
370
bool MarkBlockAsReceived (const uint256& hash) {
368
371
map<uint256, pair<NodeId, list<QueuedBlock>::iterator> >::iterator itInFlight = mapBlocksInFlight.find (hash);
369
372
if (itInFlight != mapBlocksInFlight.end ()) {
@@ -387,25 +390,37 @@ bool MarkBlockAsReceived(const uint256& hash) {
387
390
}
388
391
389
392
// Requires cs_main.
390
- void MarkBlockAsInFlight (NodeId nodeid, const uint256& hash, const Consensus::Params& consensusParams, CBlockIndex *pindex = NULL ) {
393
+ // returns false, still setting pit, if the block was already in flight from the same peer
394
+ // pit will only be valid as long as the same cs_main lock is being held
395
+ bool MarkBlockAsInFlight (NodeId nodeid, const uint256& hash, const Consensus::Params& consensusParams, CBlockIndex *pindex = NULL , list<QueuedBlock>::iterator **pit = NULL ) {
391
396
CNodeState *state = State (nodeid);
392
397
assert (state != NULL );
393
398
399
+ // Short-circuit most stuff in case its from the same node
400
+ map<uint256, pair<NodeId, list<QueuedBlock>::iterator> >::iterator itInFlight = mapBlocksInFlight.find (hash);
401
+ if (itInFlight != mapBlocksInFlight.end () && itInFlight->second .first == nodeid) {
402
+ *pit = &itInFlight->second .second ;
403
+ return false ;
404
+ }
405
+
394
406
// Make sure it's not listed somewhere already.
395
407
MarkBlockAsReceived (hash);
396
408
397
- QueuedBlock newentry = {hash, pindex, pindex != NULL };
398
- list<QueuedBlock>::iterator it = state-> vBlocksInFlight . insert (state-> vBlocksInFlight . end (), newentry );
409
+ list< QueuedBlock>::iterator it = state-> vBlocksInFlight . insert (state-> vBlocksInFlight . end (),
410
+ {hash, pindex, pindex != NULL , std::unique_ptr<PartiallyDownloadedBlock>(pit ? new PartiallyDownloadedBlock (&mempool) : NULL )} );
399
411
state->nBlocksInFlight ++;
400
- state->nBlocksInFlightValidHeaders += newentry. fValidatedHeaders ;
412
+ state->nBlocksInFlightValidHeaders += it-> fValidatedHeaders ;
401
413
if (state->nBlocksInFlight == 1 ) {
402
414
// We're starting a block download (batch) from this peer.
403
415
state->nDownloadingSince = GetTimeMicros ();
404
416
}
405
417
if (state->nBlocksInFlightValidHeaders == 1 && pindex != NULL ) {
406
418
nPeersWithValidatedDownloads++;
407
419
}
408
- mapBlocksInFlight[hash] = std::make_pair (nodeid, it);
420
+ itInFlight = mapBlocksInFlight.insert (std::make_pair (hash, std::make_pair (nodeid, it))).first ;
421
+ if (pit)
422
+ *pit = &itInFlight->second .second ;
423
+ return true ;
409
424
}
410
425
411
426
/* * Check whether the last unknown block a peer advertised is not yet known. */
@@ -4783,6 +4798,16 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
4783
4798
// nodes)
4784
4799
pfrom->PushMessage (NetMsgType::SENDHEADERS);
4785
4800
}
4801
+ if (pfrom->nVersion >= SHORT_IDS_BLOCKS_VERSION) {
4802
+ // Tell our peer we are willing to provide version-1 cmpctblocks
4803
+ // However, we do not request new block announcements using
4804
+ // cmpctblock messages.
4805
+ // We send this to non-NODE NETWORK peers as well, because
4806
+ // they may wish to request compact blocks from us
4807
+ bool fAnnounceUsingCMPCTBLOCK = false ;
4808
+ uint64_t nCMPCTBLOCKVersion = 1 ;
4809
+ pfrom->PushMessage (NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK , nCMPCTBLOCKVersion);
4810
+ }
4786
4811
}
4787
4812
4788
4813
@@ -4915,7 +4940,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
4915
4940
CNodeState *nodestate = State (pfrom->GetId ());
4916
4941
if (CanDirectFetch (chainparams.GetConsensus ()) &&
4917
4942
nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
4918
- vToFetch.push_back (inv);
4943
+ if (nodestate->fProvidesHeaderAndIDs )
4944
+ vToFetch.push_back (CInv (MSG_CMPCT_BLOCK, inv.hash ));
4945
+ else
4946
+ vToFetch.push_back (inv);
4919
4947
// Mark block as in flight already, even though the actual "getdata" message only goes out
4920
4948
// later (within the same cs_main lock, though).
4921
4949
MarkBlockAsInFlight (pfrom->GetId (), inv.hash , chainparams.GetConsensus ());
@@ -5232,6 +5260,174 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
5232
5260
}
5233
5261
5234
5262
5263
+ else if (strCommand == NetMsgType::CMPCTBLOCK && !fImporting && !fReindex ) // Ignore blocks received while importing
5264
+ {
5265
+ CBlockHeaderAndShortTxIDs cmpctblock;
5266
+ vRecv >> cmpctblock;
5267
+
5268
+ LOCK (cs_main);
5269
+
5270
+ if (mapBlockIndex.find (cmpctblock.header .hashPrevBlock ) == mapBlockIndex.end ()) {
5271
+ // Doesn't connect (or is genesis), instead of DoSing in AcceptBlockHeader, request deeper headers
5272
+ if (!IsInitialBlockDownload ())
5273
+ pfrom->PushMessage (NetMsgType::GETHEADERS, chainActive.GetLocator (pindexBestHeader), uint256 ());
5274
+ return true ;
5275
+ }
5276
+
5277
+ CBlockIndex *pindex = NULL ;
5278
+ CValidationState state;
5279
+ if (!AcceptBlockHeader (cmpctblock.header , state, chainparams, &pindex)) {
5280
+ int nDoS;
5281
+ if (state.IsInvalid (nDoS)) {
5282
+ if (nDoS > 0 )
5283
+ Misbehaving (pfrom->GetId (), nDoS);
5284
+ LogPrintf (" Peer %d sent us invalid header via cmpctblock\n " , pfrom->id );
5285
+ return true ;
5286
+ }
5287
+ }
5288
+
5289
+ // If AcceptBlockHeader returned true, it set pindex
5290
+ assert (pindex);
5291
+ UpdateBlockAvailability (pfrom->GetId (), pindex->GetBlockHash ());
5292
+
5293
+ std::map<uint256, pair<NodeId, list<QueuedBlock>::iterator> >::iterator blockInFlightIt = mapBlocksInFlight.find (pindex->GetBlockHash ());
5294
+ bool fAlreadyInFlight = blockInFlightIt != mapBlocksInFlight.end ();
5295
+
5296
+ if (pindex->nStatus & BLOCK_HAVE_DATA) // Nothing to do here
5297
+ return true ;
5298
+
5299
+ if (pindex->nChainWork <= chainActive.Tip ()->nChainWork || // We know something better
5300
+ pindex->nTx != 0 ) { // We had this block at some point, but pruned it
5301
+ if (fAlreadyInFlight ) {
5302
+ // We requested this block for some reason, but our mempool will probably be useless
5303
+ // so we just grab the block via normal getdata
5304
+ std::vector<CInv> vInv (1 );
5305
+ vInv[0 ] = CInv (MSG_BLOCK, cmpctblock.header .GetHash ());
5306
+ pfrom->PushMessage (NetMsgType::GETDATA, vInv);
5307
+ return true ;
5308
+ }
5309
+ }
5310
+
5311
+ // If we're not close to tip yet, give up and let parallel block fetch work its magic
5312
+ if (!fAlreadyInFlight && !CanDirectFetch (chainparams.GetConsensus ()))
5313
+ return true ;
5314
+
5315
+ CNodeState *nodestate = State (pfrom->GetId ());
5316
+
5317
+ // We want to be a bit conservative just to be extra careful about DoS
5318
+ // possibilities in compact block processing...
5319
+ if (pindex->nHeight <= chainActive.Height () + 2 ) {
5320
+ if ((!fAlreadyInFlight && nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) ||
5321
+ (fAlreadyInFlight && blockInFlightIt->second .first == pfrom->GetId ())) {
5322
+ list<QueuedBlock>::iterator *queuedBlockIt = NULL ;
5323
+ if (!MarkBlockAsInFlight (pfrom->GetId (), pindex->GetBlockHash (), chainparams.GetConsensus (), pindex, &queuedBlockIt)) {
5324
+ if (!(*queuedBlockIt)->partialBlock )
5325
+ (*queuedBlockIt)->partialBlock .reset (new PartiallyDownloadedBlock (&mempool));
5326
+ else {
5327
+ // The block was already in flight using compact blocks from the same peer
5328
+ LogPrint (" net" , " Peer sent us compact block we were already syncing!\n " );
5329
+ return true ;
5330
+ }
5331
+ }
5332
+
5333
+ PartiallyDownloadedBlock& partialBlock = *(*queuedBlockIt)->partialBlock ;
5334
+ ReadStatus status = partialBlock.InitData (cmpctblock);
5335
+ if (status == READ_STATUS_INVALID) {
5336
+ MarkBlockAsReceived (pindex->GetBlockHash ()); // Reset in-flight state in case of whitelist
5337
+ Misbehaving (pfrom->GetId (), 100 );
5338
+ LogPrintf (" Peer %d sent us invalid compact block\n " , pfrom->id );
5339
+ return true ;
5340
+ } else if (status == READ_STATUS_FAILED) {
5341
+ // Duplicate txindexes, the block is now in-flight, so just request it
5342
+ std::vector<CInv> vInv (1 );
5343
+ vInv[0 ] = CInv (MSG_BLOCK, cmpctblock.header .GetHash ());
5344
+ pfrom->PushMessage (NetMsgType::GETDATA, vInv);
5345
+ return true ;
5346
+ }
5347
+
5348
+ BlockTransactionsRequest req;
5349
+ for (size_t i = 0 ; i < cmpctblock.BlockTxCount (); i++) {
5350
+ if (!partialBlock.IsTxAvailable (i))
5351
+ req.indexes .push_back (i);
5352
+ }
5353
+ if (req.indexes .empty ()) {
5354
+ // Dirty hack to jump to BLOCKTXN code (TODO: move message handling into their own functions)
5355
+ BlockTransactions txn;
5356
+ txn.blockhash = cmpctblock.header .GetHash ();
5357
+ CDataStream blockTxnMsg (SER_NETWORK, PROTOCOL_VERSION);
5358
+ blockTxnMsg << txn;
5359
+ return ProcessMessage (pfrom, NetMsgType::BLOCKTXN, blockTxnMsg, nTimeReceived, chainparams);
5360
+ } else {
5361
+ req.blockhash = pindex->GetBlockHash ();
5362
+ pfrom->PushMessage (NetMsgType::GETBLOCKTXN, req);
5363
+ }
5364
+ }
5365
+ } else {
5366
+ if (fAlreadyInFlight ) {
5367
+ // We requested this block, but its far into the future, so our
5368
+ // mempool will probably be useless - request the block normally
5369
+ std::vector<CInv> vInv (1 );
5370
+ vInv[0 ] = CInv (MSG_BLOCK, cmpctblock.header .GetHash ());
5371
+ pfrom->PushMessage (NetMsgType::GETDATA, vInv);
5372
+ return true ;
5373
+ } else {
5374
+ // If this was an announce-cmpctblock, we want the same treatment as a header message
5375
+ // Dirty hack to process as if it were just a headers message (TODO: move message handling into their own functions)
5376
+ std::vector<CBlock> headers;
5377
+ headers.push_back (cmpctblock.header );
5378
+ CDataStream vHeadersMsg (SER_NETWORK, PROTOCOL_VERSION);
5379
+ vHeadersMsg << headers;
5380
+ return ProcessMessage (pfrom, NetMsgType::HEADERS, vHeadersMsg, nTimeReceived, chainparams);
5381
+ }
5382
+ }
5383
+
5384
+ CheckBlockIndex (chainparams.GetConsensus ());
5385
+ }
5386
+
5387
+ else if (strCommand == NetMsgType::BLOCKTXN && !fImporting && !fReindex ) // Ignore blocks received while importing
5388
+ {
5389
+ BlockTransactions resp;
5390
+ vRecv >> resp;
5391
+
5392
+ LOCK (cs_main);
5393
+
5394
+ map<uint256, pair<NodeId, list<QueuedBlock>::iterator> >::iterator it = mapBlocksInFlight.find (resp.blockhash );
5395
+ if (it == mapBlocksInFlight.end () || !it->second .second ->partialBlock ||
5396
+ it->second .first != pfrom->GetId ()) {
5397
+ LogPrint (" net" , " Peer %d sent us block transactions for block we weren't expecting\n " , pfrom->id );
5398
+ return true ;
5399
+ }
5400
+
5401
+ PartiallyDownloadedBlock& partialBlock = *it->second .second ->partialBlock ;
5402
+ CBlock block;
5403
+ ReadStatus status = partialBlock.FillBlock (block, resp.txn );
5404
+ if (status == READ_STATUS_INVALID) {
5405
+ MarkBlockAsReceived (resp.blockhash ); // Reset in-flight state in case of whitelist
5406
+ Misbehaving (pfrom->GetId (), 100 );
5407
+ LogPrintf (" Peer %d sent us invalid compact block/non-matching block transactions\n " , pfrom->id );
5408
+ return true ;
5409
+ } else if (status == READ_STATUS_FAILED) {
5410
+ // Might have collided, fall back to getdata now :(
5411
+ std::vector<CInv> invs;
5412
+ invs.push_back (CInv (MSG_BLOCK, resp.blockhash ));
5413
+ pfrom->PushMessage (NetMsgType::GETDATA, invs);
5414
+ } else {
5415
+ CValidationState state;
5416
+ ProcessNewBlock (state, chainparams, pfrom, &block, false , NULL );
5417
+ int nDoS;
5418
+ if (state.IsInvalid (nDoS)) {
5419
+ assert (state.GetRejectCode () < REJECT_INTERNAL); // Blocks are never rejected with internal reject codes
5420
+ pfrom->PushMessage (NetMsgType::REJECT, strCommand, (unsigned char )state.GetRejectCode (),
5421
+ state.GetRejectReason ().substr (0 , MAX_REJECT_MESSAGE_LENGTH), block.GetHash ());
5422
+ if (nDoS > 0 ) {
5423
+ LOCK (cs_main);
5424
+ Misbehaving (pfrom->GetId (), nDoS);
5425
+ }
5426
+ }
5427
+ }
5428
+ }
5429
+
5430
+
5235
5431
else if (strCommand == NetMsgType::HEADERS && !fImporting && !fReindex ) // Ignore headers received while importing
5236
5432
{
5237
5433
std::vector<CBlockHeader> headers;
@@ -5334,6 +5530,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
5334
5530
pindexLast->GetBlockHash ().ToString (), pindexLast->nHeight );
5335
5531
}
5336
5532
if (vGetData.size () > 0 ) {
5533
+ if (nodestate->fProvidesHeaderAndIDs && vGetData.size () == 1 && mapBlocksInFlight.size () == 1 && pindexLast->pprev ->IsValid (BLOCK_VALID_CHAIN)) {
5534
+ vGetData[0 ] = CInv (MSG_CMPCT_BLOCK, vGetData[0 ].hash );
5535
+ }
5337
5536
pfrom->PushMessage (NetMsgType::GETDATA, vGetData);
5338
5537
}
5339
5538
}
0 commit comments