@@ -61,6 +61,8 @@ static std::vector<std::pair<uint256, CTransactionRef>> vExtraTxnForCompact GUAR
static const uint64_t RANDOMIZER_ID_ADDRESS_RELAY = 0x3cac0035b5866b90ULL ; // SHA256("main address relay")[0:8]
+static std::atomic<bool > fAutoRequestBlocks (DEFAULT_AUTOMATIC_BLOCK_REQUESTS);
+
// Internal stuff
namespace {
/* * Number of nodes with fSyncStarted. */
@@ -104,6 +106,7 @@ namespace {
const CBlockIndex* pindex; // !< Optional.
bool fValidatedHeaders ; // !< Whether this block has validated headers at the time of request.
std::unique_ptr<PartiallyDownloadedBlock> partialBlock; // !< Optional, used for CMPCTBLOCK downloads
+ bool priorityRequest; // !< Whether its a priority download
};
std::map<uint256, std::pair<NodeId, std::list<QueuedBlock>::iterator> > mapBlocksInFlight;
@@ -121,6 +124,13 @@ namespace {
MapRelay mapRelay;
/* * Expiration-time ordered list of (expire time, relay map entry) pairs, protected by cs_main). */
std::deque<std::pair<int64_t , MapRelay::iterator>> vRelayExpiration;
+
+ struct PriorityBlockRequest {
+ const CBlockIndex* pindex;
+ bool downloaded;
+ };
+
+ std::vector<PriorityBlockRequest> blocksToDownloadFirst;
} // namespace
// ////////////////////////////////////////////////////////////////////////////
@@ -309,9 +319,13 @@ void FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTime) {
}
// Requires cs_main.
-// Returns a bool indicating whether we requested this block.
+// Returns a MarkBlockAsReceivedResult struct to indicating whether we requested this block and if it was via the priority request queue
// Also used if a block was /not/ received and timed out or started with another peer
-bool MarkBlockAsReceived (const uint256& hash) {
+struct MarkBlockAsReceivedResult {
+ bool fRequested ;
+ bool fPriorityRequest ;
+ };
+const MarkBlockAsReceivedResult MarkBlockAsReceived (const uint256& hash) {
std::map<uint256, std::pair<NodeId, std::list<QueuedBlock>::iterator> >::iterator itInFlight = mapBlocksInFlight.find (hash);
if (itInFlight != mapBlocksInFlight.end ()) {
CNodeState *state = State (itInFlight->second.first );
@@ -324,19 +338,28 @@ bool MarkBlockAsReceived(const uint256& hash) {
// First block on the queue was received, update the start download time for the next one
state->nDownloadingSince = std::max (state->nDownloadingSince, GetTimeMicros ());
}
- state->vBlocksInFlight. erase ( itInFlight->second.second ) ;
+ bool priorityRequest = itInFlight->second.second ->priorityRequest ;
state->nBlocksInFlight--;
state->nStallingSince = 0 ;
+ if (priorityRequest) {
+ // mark as downloaded
+ auto it = std::find_if (blocksToDownloadFirst.begin (), blocksToDownloadFirst.end (), [&itInFlight](const PriorityBlockRequest &r) { return r.pindex == itInFlight->second.second ->pindex; });
+ if (it != blocksToDownloadFirst.end ()) {
+ (*it).downloaded = true ;
+ }
+ }
+ state->vBlocksInFlight.erase (itInFlight->second.second );
mapBlocksInFlight.erase (itInFlight);
- return true ;
+
+ return {true , priorityRequest};
}
- return false ;
+ return { false , false } ;
}
// Requires cs_main.
// returns false, still setting pit, if the block was already in flight from the same peer
// pit will only be valid as long as the same cs_main lock is being held
-bool MarkBlockAsInFlight (NodeId nodeid, const uint256& hash, const CBlockIndex* pindex = NULL , std::list<QueuedBlock>::iterator** pit = NULL ) {
+bool MarkBlockAsInFlight (NodeId nodeid, const uint256& hash, const CBlockIndex* pindex = NULL , std::list<QueuedBlock>::iterator** pit = NULL , bool priorityRequest = false ) {
CNodeState *state = State (nodeid);
assert (state != NULL );
@@ -353,7 +376,7 @@ bool MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const CBlockIndex*
MarkBlockAsReceived (hash);
std::list<QueuedBlock>::iterator it = state->vBlocksInFlight.insert (state->vBlocksInFlight.end (),
- {hash, pindex, pindex != NULL , std::unique_ptr<PartiallyDownloadedBlock>(pit ? new PartiallyDownloadedBlock (&mempool) : NULL )});
+ {hash, pindex, pindex != NULL , std::unique_ptr<PartiallyDownloadedBlock>(pit ? new PartiallyDownloadedBlock (&mempool) : NULL ), priorityRequest });
state->nBlocksInFlight++;
state->nBlocksInFlightValidHeaders += it->fValidatedHeaders ;
if (state->nBlocksInFlight == 1 ) {
@@ -454,10 +477,12 @@ bool PeerHasHeader(CNodeState *state, const CBlockIndex *pindex)
}
/* * Update pindexLastCommonBlock and add not-in-flight missing successors to vBlocks, until it has
- * at most count entries. */
-void FindNextBlocksToDownload (NodeId nodeid, unsigned int count, std::vector<const CBlockIndex*>& vBlocks, NodeId& nodeStaller, const Consensus::Params& consensusParams) {
+ * at most count entries.
+ * returns true if priority downloads where used
+ */
+bool FindNextBlocksToDownload (NodeId nodeid, unsigned int count, std::vector<const CBlockIndex*>& vBlocks, NodeId& nodeStaller, const Consensus::Params& consensusParams) {
if (count == 0 )
- return ;
+ return false ;
vBlocks.reserve (vBlocks.size () + count);
CNodeState *state = State (nodeid);
@@ -466,9 +491,26 @@ void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<con
// Make sure pindexBestKnownBlock is up to date, we'll need it.
ProcessBlockAvailability (nodeid);
+ if (!blocksToDownloadFirst.empty ()) {
+ for (const PriorityBlockRequest &r: blocksToDownloadFirst) {
+ if (r.downloaded ) continue ;
+ if (r.pindex && state->pindexBestKnownBlock != NULL && state->pindexBestKnownBlock->nHeight >= r.pindex ->nHeight && !mapBlocksInFlight.count (r.pindex ->GetBlockHash ())) {
+ vBlocks.push_back (r.pindex );
+ if (vBlocks.size () == count) {
+ return true ;
+ }
+ }
+ }
+ return true ;
+ }
+
+ if (!fAutoRequestBlocks ) {
+ return false ;
+ }
+
if (state->pindexBestKnownBlock == NULL || state->pindexBestKnownBlock->nChainWork < chainActive.Tip ()->nChainWork || state->pindexBestKnownBlock->nChainWork < UintToArith256 (consensusParams.nMinimumChainWork )) {
// This peer has nothing interesting.
- return ;
+ return false ;
}
if (state->pindexLastCommonBlock == NULL ) {
@@ -481,7 +523,7 @@ void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<con
// of its current tip anymore. Go back enough to fix that.
state->pindexLastCommonBlock = LastCommonAncestor (state->pindexLastCommonBlock, state->pindexBestKnownBlock);
if (state->pindexLastCommonBlock == state->pindexBestKnownBlock)
- return ;
+ return false ;
std::vector<const CBlockIndex*> vToFetch;
const CBlockIndex *pindexWalk = state->pindexLastCommonBlock;
@@ -510,11 +552,11 @@ void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<con
for (const CBlockIndex* pindex : vToFetch) {
if (!pindex->IsValid (BLOCK_VALID_TREE)) {
// We consider the chain that this peer is on invalid.
- return ;
+ return false ;
}
if (!State (nodeid)->fHaveWitness && IsWitnessEnabled (pindex->pprev, consensusParams)) {
// We wouldn't download this block or its descendants from this peer.
- return ;
+ return false ;
}
if (pindex->nStatus & BLOCK_HAVE_DATA || chainActive.Contains (pindex)) {
if (pindex->nChainTx)
@@ -527,18 +569,19 @@ void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<con
// We aren't able to fetch anything, but we would be if the download window was one larger.
nodeStaller = waitingfor;
}
- return ;
+ return false ;
}
vBlocks.push_back (pindex);
if (vBlocks.size () == count) {
- return ;
+ return false ;
}
} else if (waitingfor == -1 ) {
// This is the first already-in-flight block.
waitingfor = mapBlocksInFlight[pindex->GetBlockHash ()].first ;
}
}
}
+ return false ;
}
} // namespace
@@ -2338,6 +2381,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
pindexLast->nHeight);
} else {
std::vector<CInv> vGetData;
+ // Do not request blocks if autorequest is disabled
+ if (!fAutoRequestBlocks ) {
+ return true ;
+ }
// Download as much as possible, from earliest to latest.
for (const CBlockIndex *pindex : reverse_iterate (vToFetch)) {
if (nodestate->nBlocksInFlight >= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
@@ -2378,18 +2425,23 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// Such an unrequested block may still be processed, subject to the
// conditions in AcceptBlock().
bool forceProcessing = pfrom->fWhitelisted && !IsInitialBlockDownload ();
+ MarkBlockAsReceivedResult result;
const uint256 hash (pblock->GetHash ());
{
LOCK (cs_main);
// Also always process if we requested the block explicitly, as we may
// need it even though it is not a candidate for a new best tip.
- forceProcessing |= MarkBlockAsReceived (hash);
+ result = MarkBlockAsReceived (hash);
+ forceProcessing |= result.fRequested ;
// mapBlockSource is only used for sending reject messages and DoS scores,
// so the race between here and cs_main in ProcessNewBlock is fine.
mapBlockSource.emplace (hash, std::make_pair (pfrom->GetId (), true ));
}
bool fNewBlock = false ;
- ProcessNewBlock (chainparams, pblock, forceProcessing, &fNewBlock );
+ ProcessNewBlock (chainparams, pblock, forceProcessing, &fNewBlock , !result.fPriorityRequest );
+ if (result.fPriorityRequest ) {
+ ProcessPriorityRequests (pblock);
+ }
if (fNewBlock )
pfrom->nLastBlockTime = GetTime ();
}
@@ -3231,12 +3283,12 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr
if (!pto->fClient && (fFetch || !IsInitialBlockDownload ()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
std::vector<const CBlockIndex*> vToDownload;
NodeId staller = -1 ;
- FindNextBlocksToDownload (pto->GetId (), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight , vToDownload, staller, consensusParams);
+ bool priorityRequest = FindNextBlocksToDownload (pto->GetId (), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight , vToDownload, staller, consensusParams);
for (const CBlockIndex *pindex : vToDownload) {
uint32_t nFetchFlags = GetFetchFlags (pto);
vGetData.push_back (CInv (MSG_BLOCK | nFetchFlags, pindex->GetBlockHash ()));
- MarkBlockAsInFlight (pto->GetId (), pindex->GetBlockHash (), pindex);
- LogPrint (BCLog::NET, " Requesting block %s (%d) peer=%d\n " , pindex->GetBlockHash ().ToString (),
+ MarkBlockAsInFlight (pto->GetId (), pindex->GetBlockHash (), pindex, NULL , priorityRequest );
+ LogPrint (BCLog::NET, " Requesting%s block %s (%d) peer=%d\n " , (priorityRequest ? " (priority) " : " " ) , pindex->GetBlockHash ().ToString (),
pindex->nHeight, pto->GetId ());
}
if (state.nBlocksInFlight == 0 && staller != -1 ) {
@@ -3302,6 +3354,74 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr
return true ;
}
+void AddPriorityDownload (std::vector<const CBlockIndex*>& blocksToDownload) {
+ LOCK (cs_main);
+ for (const CBlockIndex* pindex: blocksToDownload) {
+ // we add blocks regardless of duplicates
+ blocksToDownloadFirst.push_back ({pindex, false });
+ }
+}
+
+void ProcessPriorityRequests (const std::shared_ptr<CBlock> blockRef) {
+ LOCK (cs_main);
+ if (blocksToDownloadFirst.empty ()) {
+ return ;
+ }
+ auto it = std::begin (blocksToDownloadFirst);
+ while (it != std::end (blocksToDownloadFirst)) {
+ std::shared_ptr<const CBlock> currentBlock;
+ const PriorityBlockRequest &r = *it;
+ // make sure we process blocks in order
+ if (!r.downloaded ) {
+ break ;
+ }
+ if (r.pindex && blockRef && blockRef->GetHash () == r.pindex ->GetBlockHash ()) {
+ // the passed in block, no need to load again from disk
+ currentBlock = blockRef;
+ }
+ else if (r.pindex ->nStatus & BLOCK_HAVE_DATA) {
+ CBlock loadBlock;
+ if (!ReadBlockFromDisk (loadBlock, r.pindex , Params ().GetConsensus ())) {
+ throw std::runtime_error (std::string (__func__) + " Can't read block from disk" );
+ }
+ currentBlock = std::make_shared<const CBlock>(loadBlock);
+ }
+ else {
+ break ;
+ }
+
+ // allow processing through signal
+ GetMainSignals ().ProcessPriorityRequest (currentBlock, r.pindex );
+ LogPrint (BCLog::NET, " process priority block request (%s) height=%d\n " , r.pindex ->GetBlockHash ().ToString (), r.pindex ->nHeight);
+
+ // remove processed block from queue
+ it = blocksToDownloadFirst.erase (std::remove_if (blocksToDownloadFirst.begin (), blocksToDownloadFirst.end (), [&r](const PriorityBlockRequest &rB) {
+ return rB.pindex == r.pindex ;
+ }), blocksToDownloadFirst.end ());
+ }
+}
+
+bool FlushPriorityDownloads () {
+ LOCK (cs_main);
+ bool ret = blocksToDownloadFirst.empty ();
+ blocksToDownloadFirst.clear ();
+ return !ret;
+}
+
+size_t CountPriorityDownloads () {
+ // return a copy
+ LOCK (cs_main);
+ return blocksToDownloadFirst.size ();
+}
+
+void SetAutoRequestBlocks (bool state) {
+ fAutoRequestBlocks = state;
+}
+
+bool isAutoRequestingBlocks () {
+ return fAutoRequestBlocks ;
+}
+
class CNetProcessingCleanup
{
public: