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