Changeset View
Changeset View
Standalone View
Standalone View
src/qt/clientmodel.cpp
// Copyright (c) 2011-2016 The Bitcoin Core developers | // Copyright (c) 2011-2016 The Bitcoin Core developers | ||||
// Distributed under the MIT software license, see the accompanying | // Distributed under the MIT software license, see the accompanying | ||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
#include "clientmodel.h" | #include "clientmodel.h" | ||||
#include "bantablemodel.h" | #include "bantablemodel.h" | ||||
#include "guiconstants.h" | #include "guiconstants.h" | ||||
#include "guiutil.h" | #include "guiutil.h" | ||||
#include "peertablemodel.h" | #include "peertablemodel.h" | ||||
#include "chain.h" | #include "chain.h" | ||||
#include "chainparams.h" | #include "chainparams.h" | ||||
#include "checkpoints.h" | #include "checkpoints.h" | ||||
#include "clientversion.h" | #include "clientversion.h" | ||||
#include "config.h" | #include "config.h" | ||||
#include "interfaces/handler.h" | |||||
#include "interfaces/node.h" | |||||
#include "net.h" | #include "net.h" | ||||
#include "txmempool.h" | #include "txmempool.h" | ||||
#include "ui_interface.h" | #include "ui_interface.h" | ||||
#include "util.h" | #include "util.h" | ||||
#include "validation.h" | #include "validation.h" | ||||
#include "warnings.h" | #include "warnings.h" | ||||
#include <cstdint> | #include <cstdint> | ||||
#include <QDebug> | #include <QDebug> | ||||
#include <QTimer> | #include <QTimer> | ||||
class CBlockIndex; | class CBlockIndex; | ||||
static int64_t nLastHeaderTipUpdateNotification = 0; | static int64_t nLastHeaderTipUpdateNotification = 0; | ||||
static int64_t nLastBlockTipUpdateNotification = 0; | static int64_t nLastBlockTipUpdateNotification = 0; | ||||
ClientModel::ClientModel(OptionsModel *_optionsModel, QObject *parent) | ClientModel::ClientModel(interfaces::Node &node, OptionsModel *_optionsModel, | ||||
: QObject(parent), optionsModel(_optionsModel), peerTableModel(0), | QObject *parent) | ||||
banTableModel(0), pollTimer(0) { | : QObject(parent), m_node(node), optionsModel(_optionsModel), | ||||
peerTableModel(0), banTableModel(0), pollTimer(0) { | |||||
cachedBestHeaderHeight = -1; | cachedBestHeaderHeight = -1; | ||||
cachedBestHeaderTime = -1; | cachedBestHeaderTime = -1; | ||||
peerTableModel = new PeerTableModel(this); | peerTableModel = new PeerTableModel(this); | ||||
banTableModel = new BanTableModel(this); | banTableModel = new BanTableModel(this); | ||||
pollTimer = new QTimer(this); | pollTimer = new QTimer(this); | ||||
connect(pollTimer, SIGNAL(timeout()), this, SLOT(updateTimer())); | connect(pollTimer, SIGNAL(timeout()), this, SLOT(updateTimer())); | ||||
pollTimer->start(MODEL_UPDATE_DELAY); | pollTimer->start(MODEL_UPDATE_DELAY); | ||||
Show All 10 Lines | int ClientModel::getNumConnections(unsigned int flags) const { | ||||
if (flags == CONNECTIONS_IN) { | if (flags == CONNECTIONS_IN) { | ||||
connections = CConnman::CONNECTIONS_IN; | connections = CConnman::CONNECTIONS_IN; | ||||
} else if (flags == CONNECTIONS_OUT) { | } else if (flags == CONNECTIONS_OUT) { | ||||
connections = CConnman::CONNECTIONS_OUT; | connections = CConnman::CONNECTIONS_OUT; | ||||
} else if (flags == CONNECTIONS_ALL) { | } else if (flags == CONNECTIONS_ALL) { | ||||
connections = CConnman::CONNECTIONS_ALL; | connections = CConnman::CONNECTIONS_ALL; | ||||
} | } | ||||
if (g_connman) { | return m_node.getNodeCount(connections); | ||||
return g_connman->GetNodeCount(connections); | |||||
} | |||||
return 0; | |||||
} | |||||
int ClientModel::getNumBlocks() const { | |||||
LOCK(cs_main); | |||||
return chainActive.Height(); | |||||
} | } | ||||
int ClientModel::getHeaderTipHeight() const { | int ClientModel::getHeaderTipHeight() const { | ||||
if (cachedBestHeaderHeight == -1) { | if (cachedBestHeaderHeight == -1) { | ||||
// make sure we initially populate the cache via a cs_main lock | // make sure we initially populate the cache via a cs_main lock | ||||
// otherwise we need to wait for a tip update | // otherwise we need to wait for a tip update | ||||
LOCK(cs_main); | int height; | ||||
if (pindexBestHeader) { | int64_t blockTime; | ||||
cachedBestHeaderHeight = pindexBestHeader->nHeight; | if (m_node.getHeaderTip(height, blockTime)) { | ||||
cachedBestHeaderTime = pindexBestHeader->GetBlockTime(); | cachedBestHeaderHeight = height; | ||||
cachedBestHeaderTime = blockTime; | |||||
} | } | ||||
} | } | ||||
return cachedBestHeaderHeight; | return cachedBestHeaderHeight; | ||||
} | } | ||||
int64_t ClientModel::getHeaderTipTime() const { | int64_t ClientModel::getHeaderTipTime() const { | ||||
if (cachedBestHeaderTime == -1) { | if (cachedBestHeaderTime == -1) { | ||||
LOCK(cs_main); | int height; | ||||
if (pindexBestHeader) { | int64_t blockTime; | ||||
cachedBestHeaderHeight = pindexBestHeader->nHeight; | if (m_node.getHeaderTip(height, blockTime)) { | ||||
cachedBestHeaderTime = pindexBestHeader->GetBlockTime(); | cachedBestHeaderHeight = height; | ||||
cachedBestHeaderTime = blockTime; | |||||
} | } | ||||
} | } | ||||
return cachedBestHeaderTime; | return cachedBestHeaderTime; | ||||
} | } | ||||
quint64 ClientModel::getTotalBytesRecv() const { | |||||
if (!g_connman) { | |||||
return 0; | |||||
} | |||||
return g_connman->GetTotalBytesRecv(); | |||||
} | |||||
quint64 ClientModel::getTotalBytesSent() const { | |||||
if (!g_connman) { | |||||
return 0; | |||||
} | |||||
return g_connman->GetTotalBytesSent(); | |||||
} | |||||
QDateTime ClientModel::getLastBlockDate() const { | |||||
LOCK(cs_main); | |||||
if (chainActive.Tip()) { | |||||
return QDateTime::fromTime_t(chainActive.Tip()->GetBlockTime()); | |||||
} | |||||
// Genesis block's time of current network | |||||
return QDateTime::fromTime_t(Params().GenesisBlock().GetBlockTime()); | |||||
} | |||||
long ClientModel::getMempoolSize() const { | |||||
return g_mempool.size(); | |||||
} | |||||
size_t ClientModel::getMempoolDynamicUsage() const { | |||||
return g_mempool.DynamicMemoryUsage(); | |||||
} | |||||
double ClientModel::getVerificationProgress(const CBlockIndex *tipIn) const { | |||||
CBlockIndex *tip = const_cast<CBlockIndex *>(tipIn); | |||||
if (!tip) { | |||||
LOCK(cs_main); | |||||
tip = chainActive.Tip(); | |||||
} | |||||
return GuessVerificationProgress(Params().TxData(), tip); | |||||
} | |||||
void ClientModel::updateTimer() { | void ClientModel::updateTimer() { | ||||
// no locking required at this point | // no locking required at this point | ||||
// the following calls will acquire the required lock | // the following calls will acquire the required lock | ||||
Q_EMIT mempoolSizeChanged(getMempoolSize(), getMempoolDynamicUsage()); | Q_EMIT mempoolSizeChanged(m_node.getMempoolSize(), | ||||
Q_EMIT bytesChanged(getTotalBytesRecv(), getTotalBytesSent()); | m_node.getMempoolDynamicUsage()); | ||||
Q_EMIT bytesChanged(m_node.getTotalBytesRecv(), m_node.getTotalBytesSent()); | |||||
} | } | ||||
void ClientModel::updateNumConnections(int numConnections) { | void ClientModel::updateNumConnections(int numConnections) { | ||||
Q_EMIT numConnectionsChanged(numConnections); | Q_EMIT numConnectionsChanged(numConnections); | ||||
} | } | ||||
void ClientModel::updateNetworkActive(bool networkActive) { | void ClientModel::updateNetworkActive(bool networkActive) { | ||||
Q_EMIT networkActiveChanged(networkActive); | Q_EMIT networkActiveChanged(networkActive); | ||||
} | } | ||||
void ClientModel::updateAlert() { | void ClientModel::updateAlert() { | ||||
Q_EMIT alertsChanged(getStatusBarWarnings()); | Q_EMIT alertsChanged(getStatusBarWarnings()); | ||||
} | } | ||||
bool ClientModel::inInitialBlockDownload() const { | |||||
return IsInitialBlockDownload(); | |||||
} | |||||
enum BlockSource ClientModel::getBlockSource() const { | enum BlockSource ClientModel::getBlockSource() const { | ||||
if (fReindex) { | if (m_node.getReindex()) { | ||||
return BlockSource::REINDEX; | return BlockSource::REINDEX; | ||||
} else if (fImporting) { | } else if (m_node.getImporting()) { | ||||
return BlockSource::DISK; | return BlockSource::DISK; | ||||
} else if (getNumConnections() > 0) { | } else if (getNumConnections() > 0) { | ||||
return BlockSource::NETWORK; | return BlockSource::NETWORK; | ||||
} | } | ||||
return BlockSource::NONE; | return BlockSource::NONE; | ||||
} | } | ||||
void ClientModel::setNetworkActive(bool active) { | |||||
if (g_connman) { | |||||
g_connman->SetNetworkActive(active); | |||||
} | |||||
} | |||||
bool ClientModel::getNetworkActive() const { | |||||
if (g_connman) { | |||||
return g_connman->GetNetworkActive(); | |||||
} | |||||
return false; | |||||
} | |||||
QString ClientModel::getStatusBarWarnings() const { | QString ClientModel::getStatusBarWarnings() const { | ||||
return QString::fromStdString(GetWarnings("gui")); | return QString::fromStdString(m_node.getWarnings("gui")); | ||||
} | } | ||||
OptionsModel *ClientModel::getOptionsModel() { | OptionsModel *ClientModel::getOptionsModel() { | ||||
return optionsModel; | return optionsModel; | ||||
} | } | ||||
PeerTableModel *ClientModel::getPeerTableModel() { | PeerTableModel *ClientModel::getPeerTableModel() { | ||||
return peerTableModel; | return peerTableModel; | ||||
▲ Show 20 Lines • Show All 58 Lines • ▼ Show 20 Lines | |||||
static void BannedListChanged(ClientModel *clientmodel) { | static void BannedListChanged(ClientModel *clientmodel) { | ||||
qDebug() << QString("%1: Requesting update for peer banlist").arg(__func__); | qDebug() << QString("%1: Requesting update for peer banlist").arg(__func__); | ||||
QMetaObject::invokeMethod(clientmodel, "updateBanlist", | QMetaObject::invokeMethod(clientmodel, "updateBanlist", | ||||
Qt::QueuedConnection); | Qt::QueuedConnection); | ||||
} | } | ||||
static void BlockTipChanged(ClientModel *clientmodel, bool initialSync, | static void BlockTipChanged(ClientModel *clientmodel, bool initialSync, | ||||
const CBlockIndex *pIndex, bool fHeader) { | int height, int64_t blockTime, | ||||
double verificationProgress, bool fHeader) { | |||||
// lock free async UI updates in case we have a new block tip | // lock free async UI updates in case we have a new block tip | ||||
// during initial sync, only update the UI if the last update | // during initial sync, only update the UI if the last update | ||||
// was > 250ms (MODEL_UPDATE_DELAY) ago | // was > 250ms (MODEL_UPDATE_DELAY) ago | ||||
int64_t now = 0; | int64_t now = 0; | ||||
if (initialSync) { | if (initialSync) { | ||||
now = GetTimeMillis(); | now = GetTimeMillis(); | ||||
} | } | ||||
int64_t &nLastUpdateNotification = fHeader | int64_t &nLastUpdateNotification = fHeader | ||||
? nLastHeaderTipUpdateNotification | ? nLastHeaderTipUpdateNotification | ||||
: nLastBlockTipUpdateNotification; | : nLastBlockTipUpdateNotification; | ||||
if (fHeader) { | if (fHeader) { | ||||
// cache best headers time and height to reduce future cs_main locks | // cache best headers time and height to reduce future cs_main locks | ||||
clientmodel->cachedBestHeaderHeight = pIndex->nHeight; | clientmodel->cachedBestHeaderHeight = height; | ||||
clientmodel->cachedBestHeaderTime = pIndex->GetBlockTime(); | clientmodel->cachedBestHeaderTime = blockTime; | ||||
} | } | ||||
// if we are in-sync, update the UI regardless of last update time | // if we are in-sync, update the UI regardless of last update time | ||||
if (!initialSync || now - nLastUpdateNotification > MODEL_UPDATE_DELAY) { | if (!initialSync || now - nLastUpdateNotification > MODEL_UPDATE_DELAY) { | ||||
// pass a async signal to the UI thread | // pass a async signal to the UI thread | ||||
QMetaObject::invokeMethod( | QMetaObject::invokeMethod( | ||||
clientmodel, "numBlocksChanged", Qt::QueuedConnection, | clientmodel, "numBlocksChanged", Qt::QueuedConnection, | ||||
Q_ARG(int, pIndex->nHeight), | Q_ARG(int, height), | ||||
Q_ARG(QDateTime, QDateTime::fromTime_t(pIndex->GetBlockTime())), | Q_ARG(QDateTime, QDateTime::fromTime_t(blockTime)), | ||||
Q_ARG(double, clientmodel->getVerificationProgress(pIndex)), | Q_ARG(double, verificationProgress), Q_ARG(bool, fHeader)); | ||||
Q_ARG(bool, fHeader)); | |||||
nLastUpdateNotification = now; | nLastUpdateNotification = now; | ||||
} | } | ||||
} | } | ||||
void ClientModel::subscribeToCoreSignals() { | void ClientModel::subscribeToCoreSignals() { | ||||
// Connect signals to client | // Connect signals to client | ||||
uiInterface.ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2)); | m_handler_show_progress = | ||||
uiInterface.NotifyNumConnectionsChanged.connect( | m_node.handleShowProgress(boost::bind(ShowProgress, this, _1, _2)); | ||||
m_handler_notify_num_connections_changed = | |||||
m_node.handleNotifyNumConnectionsChanged( | |||||
boost::bind(NotifyNumConnectionsChanged, this, _1)); | boost::bind(NotifyNumConnectionsChanged, this, _1)); | ||||
uiInterface.NotifyNetworkActiveChanged.connect( | m_handler_notify_network_active_changed = | ||||
m_node.handleNotifyNetworkActiveChanged( | |||||
boost::bind(NotifyNetworkActiveChanged, this, _1)); | boost::bind(NotifyNetworkActiveChanged, this, _1)); | ||||
uiInterface.NotifyAlertChanged.connect( | m_handler_notify_alert_changed = | ||||
boost::bind(NotifyAlertChanged, this)); | m_node.handleNotifyAlertChanged(boost::bind(NotifyAlertChanged, this)); | ||||
uiInterface.BannedListChanged.connect(boost::bind(BannedListChanged, this)); | m_handler_banned_list_changed = | ||||
uiInterface.NotifyBlockTip.connect( | m_node.handleBannedListChanged(boost::bind(BannedListChanged, this)); | ||||
boost::bind(BlockTipChanged, this, _1, _2, false)); | m_handler_notify_block_tip = m_node.handleNotifyBlockTip( | ||||
uiInterface.NotifyHeaderTip.connect( | boost::bind(BlockTipChanged, this, _1, _2, _3, _4, false)); | ||||
boost::bind(BlockTipChanged, this, _1, _2, true)); | m_handler_notify_header_tip = m_node.handleNotifyHeaderTip( | ||||
boost::bind(BlockTipChanged, this, _1, _2, _3, _4, true)); | |||||
} | } | ||||
void ClientModel::unsubscribeFromCoreSignals() { | void ClientModel::unsubscribeFromCoreSignals() { | ||||
// Disconnect signals from client | // Disconnect signals from client | ||||
uiInterface.ShowProgress.disconnect( | m_handler_show_progress->disconnect(); | ||||
boost::bind(ShowProgress, this, _1, _2)); | m_handler_notify_num_connections_changed->disconnect(); | ||||
uiInterface.NotifyNumConnectionsChanged.disconnect( | m_handler_notify_network_active_changed->disconnect(); | ||||
boost::bind(NotifyNumConnectionsChanged, this, _1)); | m_handler_notify_alert_changed->disconnect(); | ||||
uiInterface.NotifyNetworkActiveChanged.disconnect( | m_handler_banned_list_changed->disconnect(); | ||||
boost::bind(NotifyNetworkActiveChanged, this, _1)); | m_handler_notify_block_tip->disconnect(); | ||||
uiInterface.NotifyAlertChanged.disconnect( | m_handler_notify_header_tip->disconnect(); | ||||
boost::bind(NotifyAlertChanged, this)); | |||||
uiInterface.BannedListChanged.disconnect( | |||||
boost::bind(BannedListChanged, this)); | |||||
uiInterface.NotifyBlockTip.disconnect( | |||||
boost::bind(BlockTipChanged, this, _1, _2, false)); | |||||
uiInterface.NotifyHeaderTip.disconnect( | |||||
boost::bind(BlockTipChanged, this, _1, _2, true)); | |||||
} | } |