#include "clientmodel.h" #include "guiconstants.h" #include "optionsmodel.h" #include "addresstablemodel.h" #include "transactiontablemodel.h" #include "alert.h" #include "main.h" #include "ui_interface.h" #include #include static const int64_t nClientStartupTime = GetTime(); ClientModel::ClientModel(OptionsModel *optionsModel, QObject *parent) : QObject(parent), optionsModel(optionsModel), cachedNumBlocks(0), cachedNumBlocksOfPeers(0), cachedNumConnections(0), pollTimer(0) { numBlocksAtStartup = -1; pollTimer = new QTimer(this); pollTimer->setInterval(MODEL_UPDATE_DELAY); pollTimer->start(); connect(pollTimer, SIGNAL(timeout()), this, SLOT(updateTimer())); subscribeToCoreSignals(); } ClientModel::~ClientModel() { unsubscribeFromCoreSignals(); } int ClientModel::getNumConnections() const { // Use TRY_LOCK to avoid blocking the UI thread when the network // thread holds cs_vNodes (e.g. during DNS resolution or connections). // Return the cached value if the lock is busy. TRY_LOCK(cs_vNodes, lockNodes); if (lockNodes) { cachedNumConnections = vNodes.size(); } return cachedNumConnections; } int ClientModel::getNumBlocks() const { return nBestHeight; } int ClientModel::getNumBlocksAtStartup() { if (numBlocksAtStartup == -1) numBlocksAtStartup = getNumBlocks(); return numBlocksAtStartup; } QDateTime ClientModel::getLastBlockDate() const { if (pindexBest) return QDateTime::fromTime_t(pindexBest->GetBlockTime()); else return QDateTime::fromTime_t(1393221600); // Genesis block's time } void ClientModel::updateTimer() { // Some quantities (such as number of blocks) change so fast that we don't want to be notified for each change. // Periodically check and update with a timer. int newNumBlocks = getNumBlocks(); int newNumBlocksOfPeers = getNumBlocksOfPeers(); // Always emit when values change or during IBD. // Also emit every ~30 seconds even when idle so setNumBlocks() can // re-evaluate sync status (e.g. when a new block arrives after a long gap). static int64_t nLastEmit = 0; int64_t nNow = GetTime(); bool fChanged = (cachedNumBlocks != newNumBlocks || cachedNumBlocksOfPeers != newNumBlocksOfPeers); bool fCatchingUp = (newNumBlocks < newNumBlocksOfPeers); bool fPeriodicRefresh = (nNow - nLastEmit >= 30); if(fChanged || fCatchingUp || fPeriodicRefresh) { cachedNumBlocks = newNumBlocks; cachedNumBlocksOfPeers = newNumBlocksOfPeers; nLastEmit = nNow; emit numBlocksChanged(newNumBlocks, newNumBlocksOfPeers); } } void ClientModel::updateNumConnections(int numConnections) { emit numConnectionsChanged(numConnections); } void ClientModel::updateAlert(const QString &hash, int status) { // Show error message notification for new alert if(status == CT_NEW) { uint256 hash_256; hash_256.SetHex(hash.toStdString()); CAlert alert = CAlert::getAlertByHash(hash_256); if(!alert.IsNull()) { emit error(tr("Network Alert"), QString::fromStdString(alert.strStatusBar), false); } } // Emit a numBlocksChanged when the status message changes, // so that the view recomputes and updates the status bar. emit numBlocksChanged(getNumBlocks(), getNumBlocksOfPeers()); } double ClientModel::GetDifficulty() const { // Floating point number that is a multiple of the minimum difficulty, // minimum difficulty = 1.0. if (pindexBest == NULL) return 1.0; int nShift = (pindexBest->nBits >> 24) & 0xff; double dDiff = (double)0x0000ffff / (double)(pindexBest->nBits & 0x00ffffff); while (nShift < 29) { dDiff *= 256.0; nShift++; } while (nShift > 29) { dDiff /= 256.0; nShift--; } return dDiff; } bool ClientModel::isTestNet() const { return fTestNet; } bool ClientModel::inInitialBlockDownload() const { return IsInitialBlockDownload(); } int ClientModel::getNumBlocksOfPeers() const { return GetNumBlocksOfPeers(); } QString ClientModel::getStatusBarWarnings() const { return QString::fromStdString(GetWarnings("statusbar")); } OptionsModel *ClientModel::getOptionsModel() { return optionsModel; } QString ClientModel::formatFullVersion() const { return QString::fromStdString(FormatFullVersion()); } QString ClientModel::formatBuildDate() const { return QString::fromStdString(CLIENT_DATE); } QString ClientModel::clientName() const { return QString::fromStdString(CLIENT_NAME); } QString ClientModel::formatClientStartupTime() const { return QDateTime::fromTime_t(nClientStartupTime).toString(); } // Handlers for core signals static void NotifyBlocksChanged(ClientModel *clientmodel) { // This notification is too frequent. Don't trigger a signal. // Don't remove it, though, as it might be useful later. } static void NotifyNumConnectionsChanged(ClientModel *clientmodel, int newNumConnections) { // Don't queue UI updates during shutdown - ClientModel may be destroyed // before Qt processes the queued invocation, causing use-after-free. if (fShutdown) return; QMetaObject::invokeMethod(clientmodel, "updateNumConnections", Qt::QueuedConnection, Q_ARG(int, newNumConnections)); } static void NotifyAlertChanged(ClientModel *clientmodel, const uint256 &hash, ChangeType status) { if (fShutdown) return; OutputDebugStringF("NotifyAlertChanged %s status=%i\n", hash.GetHex().c_str(), status); QMetaObject::invokeMethod(clientmodel, "updateAlert", Qt::QueuedConnection, Q_ARG(QString, QString::fromStdString(hash.GetHex())), Q_ARG(int, status)); } void ClientModel::subscribeToCoreSignals() { // Connect signals to client uiInterface.NotifyBlocksChanged.connect(boost::bind(NotifyBlocksChanged, this)); uiInterface.NotifyNumConnectionsChanged.connect(boost::bind(NotifyNumConnectionsChanged, this, _1)); uiInterface.NotifyAlertChanged.connect(boost::bind(NotifyAlertChanged, this, _1, _2)); } void ClientModel::unsubscribeFromCoreSignals() { // Disconnect signals from client uiInterface.NotifyBlocksChanged.disconnect(boost::bind(NotifyBlocksChanged, this)); uiInterface.NotifyNumConnectionsChanged.disconnect(boost::bind(NotifyNumConnectionsChanged, this, _1)); uiInterface.NotifyAlertChanged.disconnect(boost::bind(NotifyAlertChanged, this, _1, _2)); }