diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 4d832f37113..3344a77f156 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -60,6 +60,8 @@ static std::vector> vExtraTxnForCompact GUAR static const uint64_t RANDOMIZER_ID_ADDRESS_RELAY = 0x3cac0035b5866b90ULL; // SHA256("main address relay")[0:8] +static std::atomic fAutoRequestBlocks(DEFAULT_AUTOMATIC_BLOCK_REQUESTS); + // Internal stuff namespace { /** Number of nodes with fSyncStarted. */ @@ -105,6 +107,7 @@ namespace { std::unique_ptr partialBlock; //!< Optional, used for CMPCTBLOCK downloads }; std::map::iterator> > mapBlocksInFlight; + std::atomic nBlocksInFlight; //!< lock free counter on how many block are in flight /** Stack of nodes which we have set to announce using compact blocks */ std::list lNodesAnnouncingHeaderAndIDs; @@ -291,6 +294,7 @@ void FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTime) { for (const QueuedBlock& entry : state->vBlocksInFlight) { mapBlocksInFlight.erase(entry.hash); } + nBlocksInFlight = mapBlocksInFlight.size(); EraseOrphansFor(nodeid); nPreferredDownload -= state->fPreferredDownload; nPeersWithValidatedDownloads -= (state->nBlocksInFlightValidHeaders != 0); @@ -327,6 +331,7 @@ bool MarkBlockAsReceived(const uint256& hash) { state->nBlocksInFlight--; state->nStallingSince = 0; mapBlocksInFlight.erase(itInFlight); + nBlocksInFlight = mapBlocksInFlight.size(); return true; } return false; @@ -363,6 +368,7 @@ bool MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const CBlockIndex* nPeersWithValidatedDownloads++; } itInFlight = mapBlocksInFlight.insert(std::make_pair(hash, std::make_pair(nodeid, it))).first; + nBlocksInFlight = mapBlocksInFlight.size(); if (pit) *pit = &itInFlight->second.second; return true; @@ -465,6 +471,10 @@ void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vectorpindexBestKnownBlock == NULL || state->pindexBestKnownBlock->nChainWork < chainActive.Tip()->nChainWork || state->pindexBestKnownBlock->nChainWork < UintToArith256(consensusParams.nMinimumChainWork)) { // This peer has nothing interesting. return; @@ -3301,6 +3311,18 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic& interr return true; } +void SetAutoRequestBlocks(bool state) { + fAutoRequestBlocks = state; +} + +bool isAutoRequestingBlocks() { + return fAutoRequestBlocks; +} + +unsigned int getAmountOfBlocksInFlight() { + return nBlocksInFlight; +} + class CNetProcessingCleanup { public: diff --git a/src/net_processing.h b/src/net_processing.h index db6d81e6b67..c55c2c74934 100644 --- a/src/net_processing.h +++ b/src/net_processing.h @@ -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,10 @@ bool ProcessMessages(CNode* pfrom, CConnman& connman, const std::atomic& i */ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic& interrupt); +void SetAutoRequestBlocks(bool); +bool isAutoRequestingBlocks(); + +/** retruns the amount of blocks in flight (in total) */ +unsigned int getAmountOfBlocksInFlight(); + #endif // BITCOIN_NET_PROCESSING_H diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 429c18cba85..75b83026576 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -479,6 +479,10 @@ void BitcoinGUI::setClientModel(ClientModel *_clientModel) connect(_clientModel, SIGNAL(networkActiveChanged(bool)), this, SLOT(setNetworkActive(bool))); modalOverlay->setKnownBestHeight(_clientModel->getHeaderTipHeight(), QDateTime::fromTime_t(_clientModel->getHeaderTipTime())); + modalOverlay->setPauseResumeState(!_clientModel->isAutoRequestingBlocks()); + connect(modalOverlay, SIGNAL(requestVerificationPauseOrResume()), _clientModel, SLOT(toggleAutoRequestBlocks())); + connect(_clientModel, SIGNAL(verificationProgressPauseStateHasChanged(bool)), modalOverlay, SLOT(setPauseResumeState(bool))); + setNumBlocks(_clientModel->getNumBlocks(), _clientModel->getLastBlockDate(), _clientModel->getVerificationProgress(NULL), false); connect(_clientModel, SIGNAL(numBlocksChanged(int,QDateTime,double,bool)), this, SLOT(setNumBlocks(int,QDateTime,double,bool))); @@ -506,6 +510,7 @@ void BitcoinGUI::setClientModel(ClientModel *_clientModel) // initialize the disable state of the tray icon with the current value in the model. setTrayIconVisible(optionsModel->getHideTrayIcon()); } + } else { // Disable possibility to show main window via action toggleHideAction->setEnabled(false); diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 33f4535ee22..b7e93e4202c 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -15,6 +15,7 @@ #include "clientversion.h" #include "validation.h" #include "net.h" +#include "net_processing.h" #include "txmempool.h" #include "ui_interface.h" #include "util.h" @@ -245,6 +246,22 @@ QString ClientModel::dataDir() const return GUIUtil::boostPathToQString(GetDataDir()); } +bool ClientModel::isAutoRequestingBlocks() const +{ + return ::isAutoRequestingBlocks(); +} + +void ClientModel::setAutoRequestBlocks(bool state) +{ + ::SetAutoRequestBlocks(state); + Q_EMIT verificationProgressPauseStateHasChanged(!::isAutoRequestingBlocks()); +} + +void ClientModel::toggleAutoRequestBlocks() +{ + setAutoRequestBlocks(!::isAutoRequestingBlocks()); +} + void ClientModel::updateBanlist() { banTableModel->refresh(); diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index 6447cae1bb4..b4ae7fcf753 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -84,6 +84,10 @@ class ClientModel : public QObject mutable std::atomic cachedBestHeaderHeight; mutable std::atomic cachedBestHeaderTime; + // get/set state about autorequesting-blocks during IBD + bool isAutoRequestingBlocks() const; + void setAutoRequestBlocks(bool state); + private: OptionsModel *optionsModel; PeerTableModel *peerTableModel; @@ -108,12 +112,15 @@ class ClientModel : public QObject // Show progress dialog e.g. for verifychain void showProgress(const QString &title, int nProgress); + void verificationProgressPauseStateHasChanged(bool pauseActive); + public Q_SLOTS: void updateTimer(); void updateNumConnections(int numConnections); void updateNetworkActive(bool networkActive); void updateAlert(); void updateBanlist(); + void toggleAutoRequestBlocks(); }; #endif // BITCOIN_QT_CLIENTMODEL_H diff --git a/src/qt/forms/modaloverlay.ui b/src/qt/forms/modaloverlay.ui index 65a7a6c77ee..21665a7b943 100644 --- a/src/qt/forms/modaloverlay.ui +++ b/src/qt/forms/modaloverlay.ui @@ -224,6 +224,26 @@ QLabel { color: rgb(40,40,40); } + + + + 75 + true + + + + Blocks requested from peers + + + + + + + Unknown... + + + + @@ -236,7 +256,7 @@ QLabel { color: rgb(40,40,40); } - + @@ -249,7 +269,7 @@ QLabel { color: rgb(40,40,40); } - + @@ -262,7 +282,7 @@ QLabel { color: rgb(40,40,40); } - + @@ -283,7 +303,7 @@ QLabel { color: rgb(40,40,40); } - + @@ -296,14 +316,14 @@ QLabel { color: rgb(40,40,40); } - + calculating... - + @@ -316,7 +336,7 @@ QLabel { color: rgb(40,40,40); } - + calculating... @@ -334,6 +354,33 @@ QLabel { color: rgb(40,40,40); } 10 + + + Pause downloading blocks + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Qt::Horizontal diff --git a/src/qt/modaloverlay.cpp b/src/qt/modaloverlay.cpp index a83f285034f..d767681d0d5 100644 --- a/src/qt/modaloverlay.cpp +++ b/src/qt/modaloverlay.cpp @@ -8,6 +8,7 @@ #include "guiutil.h" #include "chainparams.h" +#include "net_processing.h" #include #include @@ -18,10 +19,12 @@ ui(new Ui::ModalOverlay), bestHeaderHeight(0), bestHeaderDate(QDateTime()), layerIsVisible(false), -userClosed(false) +userClosed(false), +verificationPauseActive(false) { ui->setupUi(this); connect(ui->closeButton, SIGNAL(clicked()), this, SLOT(closeClicked())); + connect(ui->pauseResumeVerification, SIGNAL(clicked()), this, SLOT(pauseClicked())); if (parent) { parent->installEventFilter(this); raise(); @@ -71,6 +74,7 @@ void ModalOverlay::setKnownBestHeight(int count, const QDateTime& blockDate) if (count > bestHeaderHeight) { bestHeaderHeight = count; bestHeaderDate = blockDate; + eventuallyShowHeaderSyncing(count); } } @@ -125,15 +129,30 @@ void ModalOverlay::tipUpdate(int count, const QDateTime& blockDate, double nVeri // not syncing return; + // show remaining number of blocks + ui->numberOfBlocksLeft->setText(QString::number(bestHeaderHeight - count)); + + // show already requested blocks (in total) + ui->numberBlocksRequested->setText(QString::number(getAmountOfBlocksInFlight())); + eventuallyShowHeaderSyncing(count); + updatePauseState(verificationPauseActive); + + // disable pause button when we we can fetch directly + // avoid using the core-layer's existing CanFetchDirectly() + bool canFetchDirecly = (blockDate.toTime_t() > GetAdjustedTime() - Params().GetConsensus().nPowTargetSpacing * 20); + ui->pauseResumeVerification->setEnabled(!canFetchDirecly); + ui->infoLabel->setVisible(!canFetchDirecly); +} + +void ModalOverlay::eventuallyShowHeaderSyncing(int count) +{ // estimate the number of headers left based on nPowTargetSpacing // and check if the gui is not aware of the best header (happens rarely) - int estimateNumHeadersLeft = bestHeaderDate.secsTo(currentDate) / Params().GetConsensus().nPowTargetSpacing; + int estimateNumHeadersLeft = bestHeaderDate.secsTo(QDateTime::currentDateTime()) / Params().GetConsensus().nPowTargetSpacing; bool hasBestHeader = bestHeaderHeight >= count; - // show remaining number of blocks - if (estimateNumHeadersLeft < HEADER_HEIGHT_DELTA_SYNC && hasBestHeader) { - ui->numberOfBlocksLeft->setText(QString::number(bestHeaderHeight - count)); - } else { + // show headers-syncing progress if we still sync headers + if (estimateNumHeadersLeft >= HEADER_HEIGHT_DELTA_SYNC || !hasBestHeader) { ui->numberOfBlocksLeft->setText(tr("Unknown. Syncing Headers (%1)...").arg(bestHeaderHeight)); ui->expectedTimeLeft->setText(tr("Unknown...")); } @@ -170,3 +189,21 @@ void ModalOverlay::closeClicked() showHide(true); userClosed = true; } + +void ModalOverlay::pauseClicked() +{ + Q_EMIT requestVerificationPauseOrResume(); +} + +void ModalOverlay::setPauseResumeState(bool pauseActive) +{ + verificationPauseActive = pauseActive; + updatePauseState(pauseActive); +} + +void ModalOverlay::updatePauseState(bool pauseActive) +{ + ui->labelNumberBlocksRequested->setText((pauseActive ? "Finish downloading blocks": "Blocks requested from peers")); + ui->pauseResumeVerification->setText((pauseActive ? "Resume downloading blocks ": "Pause downloading blocks")); + ui->infoLabel->setText((pauseActive && getAmountOfBlocksInFlight() > 0 ? "Wait to finish current downloads...": "")); +} diff --git a/src/qt/modaloverlay.h b/src/qt/modaloverlay.h index 21ccdbd8397..1b773ebbbea 100644 --- a/src/qt/modaloverlay.h +++ b/src/qt/modaloverlay.h @@ -32,8 +32,14 @@ public Q_SLOTS: // will show or hide the modal layer void showHide(bool hide = false, bool userRequested = false); void closeClicked(); + void pauseClicked(); + void setPauseResumeState(bool pauseActive); bool isLayerVisible() { return layerIsVisible; } +Q_SIGNALS: + // Fired when the user requested a block download pause or resume + void requestVerificationPauseOrResume(); + protected: bool eventFilter(QObject * obj, QEvent * ev); bool event(QEvent* ev); @@ -45,6 +51,9 @@ public Q_SLOTS: QVector > blockProcessTime; bool layerIsVisible; bool userClosed; + bool verificationPauseActive; + void eventuallyShowHeaderSyncing(int count); + void updatePauseState(bool pauseActive); }; #endif // BITCOIN_QT_MODALOVERLAY_H