1 // Copyright (c) 2011-2015 The Bitcoin Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5 #include "clientmodel.h"
6
7 #include "bantablemodel.h"
8 #include "guiconstants.h"
9 #include "guiutil.h"
10 #include "peertablemodel.h"
11
12 #include "chainparams.h"
13 #include "checkpoints.h"
14 #include "clientversion.h"
15 #include "net.h"
16 #include "txmempool.h"
17 #include "ui_interface.h"
18 #include "util.h"
19
20 #include <stdint.h>
21
22 #include <QDebug>
23 #include <QTimer>
24
25 class CBlockIndex;
26
27 static const int64_t nClientStartupTime = GetTime();
28 static int64_t nLastHeaderTipUpdateNotification = 0;
29 static int64_t nLastBlockTipUpdateNotification = 0;
30
ClientModel(OptionsModel * optionsModel,QObject * parent)31 ClientModel::ClientModel(OptionsModel *optionsModel, QObject *parent) :
32 QObject(parent),
33 optionsModel(optionsModel),
34 peerTableModel(0),
35 banTableModel(0),
36 pollTimer(0)
37 {
38 peerTableModel = new PeerTableModel(this);
39 banTableModel = new BanTableModel(this);
40 pollTimer = new QTimer(this);
41 connect(pollTimer, SIGNAL(timeout()), this, SLOT(updateTimer()));
42 pollTimer->start(MODEL_UPDATE_DELAY);
43
44 subscribeToCoreSignals();
45 }
46
~ClientModel()47 ClientModel::~ClientModel()
48 {
49 unsubscribeFromCoreSignals();
50 }
51
getNumConnections(unsigned int flags) const52 int ClientModel::getNumConnections(unsigned int flags) const
53 {
54 LOCK(cs_vNodes);
55 if (flags == CONNECTIONS_ALL) // Shortcut if we want total
56 return vNodes.size();
57
58 int nNum = 0;
59 BOOST_FOREACH(const CNode* pnode, vNodes)
60 if (flags & (pnode->fInbound ? CONNECTIONS_IN : CONNECTIONS_OUT))
61 nNum++;
62
63 return nNum;
64 }
65
getNumBlocks() const66 int ClientModel::getNumBlocks() const
67 {
68 LOCK(cs_main);
69 return chainActive.Height();
70 }
71
getTotalBytesRecv() const72 quint64 ClientModel::getTotalBytesRecv() const
73 {
74 return CNode::GetTotalBytesRecv();
75 }
76
getTotalBytesSent() const77 quint64 ClientModel::getTotalBytesSent() const
78 {
79 return CNode::GetTotalBytesSent();
80 }
81
getLastBlockDate() const82 QDateTime ClientModel::getLastBlockDate() const
83 {
84 LOCK(cs_main);
85
86 if (chainActive.Tip())
87 return QDateTime::fromTime_t(chainActive.Tip()->GetBlockTime());
88
89 return QDateTime::fromTime_t(Params().GenesisBlock().GetBlockTime()); // Genesis block's time of current network
90 }
91
getMempoolSize() const92 long ClientModel::getMempoolSize() const
93 {
94 return mempool.size();
95 }
96
getMempoolDynamicUsage() const97 size_t ClientModel::getMempoolDynamicUsage() const
98 {
99 return mempool.DynamicMemoryUsage();
100 }
101
getVerificationProgress(const CBlockIndex * tipIn) const102 double ClientModel::getVerificationProgress(const CBlockIndex *tipIn) const
103 {
104 CBlockIndex *tip = const_cast<CBlockIndex *>(tipIn);
105 if (!tip)
106 {
107 LOCK(cs_main);
108 tip = chainActive.Tip();
109 }
110 return Checkpoints::GuessVerificationProgress(Params().Checkpoints(), tip);
111 }
112
updateTimer()113 void ClientModel::updateTimer()
114 {
115 // no locking required at this point
116 // the following calls will acquire the required lock
117 Q_EMIT mempoolSizeChanged(getMempoolSize(), getMempoolDynamicUsage());
118 Q_EMIT bytesChanged(getTotalBytesRecv(), getTotalBytesSent());
119 }
120
updateNumConnections(int numConnections)121 void ClientModel::updateNumConnections(int numConnections)
122 {
123 Q_EMIT numConnectionsChanged(numConnections);
124 }
125
updateAlert()126 void ClientModel::updateAlert()
127 {
128 Q_EMIT alertsChanged(getStatusBarWarnings());
129 }
130
inInitialBlockDownload() const131 bool ClientModel::inInitialBlockDownload() const
132 {
133 return IsInitialBlockDownload();
134 }
135
getBlockSource() const136 enum BlockSource ClientModel::getBlockSource() const
137 {
138 if (fReindex)
139 return BLOCK_SOURCE_REINDEX;
140 else if (fImporting)
141 return BLOCK_SOURCE_DISK;
142 else if (getNumConnections() > 0)
143 return BLOCK_SOURCE_NETWORK;
144
145 return BLOCK_SOURCE_NONE;
146 }
147
getStatusBarWarnings() const148 QString ClientModel::getStatusBarWarnings() const
149 {
150 return QString::fromStdString(GetWarnings("gui"));
151 }
152
getOptionsModel()153 OptionsModel *ClientModel::getOptionsModel()
154 {
155 return optionsModel;
156 }
157
getPeerTableModel()158 PeerTableModel *ClientModel::getPeerTableModel()
159 {
160 return peerTableModel;
161 }
162
getBanTableModel()163 BanTableModel *ClientModel::getBanTableModel()
164 {
165 return banTableModel;
166 }
167
formatFullVersion() const168 QString ClientModel::formatFullVersion() const
169 {
170 return QString::fromStdString(FormatFullVersion());
171 }
172
formatSubVersion() const173 QString ClientModel::formatSubVersion() const
174 {
175 return QString::fromStdString(strSubVersion);
176 }
177
isReleaseVersion() const178 bool ClientModel::isReleaseVersion() const
179 {
180 return CLIENT_VERSION_IS_RELEASE;
181 }
182
formatClientStartupTime() const183 QString ClientModel::formatClientStartupTime() const
184 {
185 return QDateTime::fromTime_t(nClientStartupTime).toString();
186 }
187
dataDir() const188 QString ClientModel::dataDir() const
189 {
190 return GUIUtil::boostPathToQString(GetDataDir());
191 }
192
updateBanlist()193 void ClientModel::updateBanlist()
194 {
195 banTableModel->refresh();
196 }
197
198 // Handlers for core signals
ShowProgress(ClientModel * clientmodel,const std::string & title,int nProgress)199 static void ShowProgress(ClientModel *clientmodel, const std::string &title, int nProgress)
200 {
201 // emits signal "showProgress"
202 QMetaObject::invokeMethod(clientmodel, "showProgress", Qt::QueuedConnection,
203 Q_ARG(QString, QString::fromStdString(title)),
204 Q_ARG(int, nProgress));
205 }
206
NotifyNumConnectionsChanged(ClientModel * clientmodel,int newNumConnections)207 static void NotifyNumConnectionsChanged(ClientModel *clientmodel, int newNumConnections)
208 {
209 // Too noisy: qDebug() << "NotifyNumConnectionsChanged: " + QString::number(newNumConnections);
210 QMetaObject::invokeMethod(clientmodel, "updateNumConnections", Qt::QueuedConnection,
211 Q_ARG(int, newNumConnections));
212 }
213
NotifyAlertChanged(ClientModel * clientmodel)214 static void NotifyAlertChanged(ClientModel *clientmodel)
215 {
216 qDebug() << "NotifyAlertChanged";
217 QMetaObject::invokeMethod(clientmodel, "updateAlert", Qt::QueuedConnection);
218 }
219
BannedListChanged(ClientModel * clientmodel)220 static void BannedListChanged(ClientModel *clientmodel)
221 {
222 qDebug() << QString("%1: Requesting update for peer banlist").arg(__func__);
223 QMetaObject::invokeMethod(clientmodel, "updateBanlist", Qt::QueuedConnection);
224 }
225
BlockTipChanged(ClientModel * clientmodel,bool initialSync,const CBlockIndex * pIndex,bool fHeader)226 static void BlockTipChanged(ClientModel *clientmodel, bool initialSync, const CBlockIndex *pIndex, bool fHeader)
227 {
228 // lock free async UI updates in case we have a new block tip
229 // during initial sync, only update the UI if the last update
230 // was > 250ms (MODEL_UPDATE_DELAY) ago
231 int64_t now = 0;
232 if (initialSync)
233 now = GetTimeMillis();
234
235 int64_t& nLastUpdateNotification = fHeader ? nLastHeaderTipUpdateNotification : nLastBlockTipUpdateNotification;
236
237 // if we are in-sync, update the UI regardless of last update time
238 if (!initialSync || now - nLastUpdateNotification > MODEL_UPDATE_DELAY) {
239 //pass a async signal to the UI thread
240 QMetaObject::invokeMethod(clientmodel, "numBlocksChanged", Qt::QueuedConnection,
241 Q_ARG(int, pIndex->nHeight),
242 Q_ARG(QDateTime, QDateTime::fromTime_t(pIndex->GetBlockTime())),
243 Q_ARG(double, clientmodel->getVerificationProgress(pIndex)),
244 Q_ARG(bool, fHeader));
245 nLastUpdateNotification = now;
246 }
247 }
248
subscribeToCoreSignals()249 void ClientModel::subscribeToCoreSignals()
250 {
251 // Connect signals to client
252 uiInterface.ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2));
253 uiInterface.NotifyNumConnectionsChanged.connect(boost::bind(NotifyNumConnectionsChanged, this, _1));
254 uiInterface.NotifyAlertChanged.connect(boost::bind(NotifyAlertChanged, this));
255 uiInterface.BannedListChanged.connect(boost::bind(BannedListChanged, this));
256 uiInterface.NotifyBlockTip.connect(boost::bind(BlockTipChanged, this, _1, _2, false));
257 uiInterface.NotifyHeaderTip.connect(boost::bind(BlockTipChanged, this, _1, _2, true));
258 }
259
unsubscribeFromCoreSignals()260 void ClientModel::unsubscribeFromCoreSignals()
261 {
262 // Disconnect signals from client
263 uiInterface.ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2));
264 uiInterface.NotifyNumConnectionsChanged.disconnect(boost::bind(NotifyNumConnectionsChanged, this, _1));
265 uiInterface.NotifyAlertChanged.disconnect(boost::bind(NotifyAlertChanged, this));
266 uiInterface.BannedListChanged.disconnect(boost::bind(BannedListChanged, this));
267 uiInterface.NotifyBlockTip.disconnect(boost::bind(BlockTipChanged, this, _1, _2, false));
268 uiInterface.NotifyHeaderTip.disconnect(boost::bind(BlockTipChanged, this, _1, _2, true));
269 }
270