Add simple light-client mode (RPC only) #10794

Open
wants to merge 10 commits into
from
View
@@ -338,6 +338,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-?", _("Print this help message and exit"));
strUsage += HelpMessageOpt("-version", _("Print version and exit"));
strUsage += HelpMessageOpt("-alertnotify=<cmd>", _("Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)"));
+ strUsage += HelpMessageOpt("-autorequestblocks", strprintf(_("Automatic block request, if disabled, blocks will not be requested automatically (default: %u)"), DEFAULT_AUTOMATIC_BLOCK_REQUESTS));
strUsage += HelpMessageOpt("-blocknotify=<cmd>", _("Execute command when the best block changes (%s in cmd is replaced by block hash)"));
if (showDebug)
strUsage += HelpMessageOpt("-blocksonly", strprintf(_("Whether to operate in a blocks only mode (default: %u)"), DEFAULT_BLOCKSONLY));
@@ -970,6 +971,7 @@ bool AppInitParameterInteraction()
}
fCheckBlockIndex = GetBoolArg("-checkblockindex", chainparams.DefaultConsistencyChecks());
fCheckpointsEnabled = GetBoolArg("-checkpoints", DEFAULT_CHECKPOINTS_ENABLED);
+ SetAutoRequestBlocks(GetBoolArg("-autorequestblocks", DEFAULT_AUTOMATIC_BLOCK_REQUESTS));
hashAssumeValid = uint256S(GetArg("-assumevalid", chainparams.GetConsensus().defaultAssumeValid.GetHex()));
if (!hashAssumeValid.IsNull())
View
@@ -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:
View
@@ -27,6 +27,9 @@ void RegisterNodeSignals(CNodeSignals& nodeSignals);
/** Unregister a network node */
void UnregisterNodeSignals(CNodeSignals& nodeSignals);
+/** if disabled, blocks will not be requested automatically, useful for low-resources-available mode */
+static const bool DEFAULT_AUTOMATIC_BLOCK_REQUESTS = true;
+
class PeerLogicValidation : public CValidationInterface {
private:
CConnman* connman;
@@ -64,4 +67,18 @@ bool ProcessMessages(CNode* pfrom, CConnman& connman, const std::atomic<bool>& i
*/
bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interrupt);
+/**
+ * Prioritize a block for downloading
+ * Blocks requested with priority will be downloaded and processed first
+ * Downloaded blocks will not trigger ActivateBestChain
+ */
+void AddPriorityDownload(std::vector<const CBlockIndex*>& blocksToDownload);
+void ProcessPriorityRequests(const std::shared_ptr<CBlock> block);
+bool FlushPriorityDownloads();
+size_t CountPriorityDownloads();
+void ProcessPriorityRequests(const std::shared_ptr<CBlock> block);
+
+void SetAutoRequestBlocks(bool state);
+bool isAutoRequestingBlocks();
+
#endif // BITCOIN_NET_PROCESSING_H
Oops, something went wrong.