Bitcoin Core  0.20.99
P2P Digital Currency
clientmodel.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-2020 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 <qt/clientmodel.h>
6 
7 #include <qt/bantablemodel.h>
8 #include <qt/guiconstants.h>
9 #include <qt/guiutil.h>
10 #include <qt/peertablemodel.h>
11 
12 #include <clientversion.h>
13 #include <interfaces/handler.h>
14 #include <interfaces/node.h>
15 #include <net.h>
16 #include <netbase.h>
17 #include <util/system.h>
18 #include <validation.h>
19 
20 #include <stdint.h>
21 
22 #include <QDebug>
23 #include <QThread>
24 #include <QTimer>
25 
28 
29 ClientModel::ClientModel(interfaces::Node& node, OptionsModel *_optionsModel, QObject *parent) :
30  QObject(parent),
31  m_node(node),
32  optionsModel(_optionsModel),
33  peerTableModel(nullptr),
34  banTableModel(nullptr),
35  m_thread(new QThread(this))
36 {
40  banTableModel = new BanTableModel(m_node, this);
41 
42  QTimer* timer = new QTimer;
43  timer->setInterval(MODEL_UPDATE_DELAY);
44  connect(timer, &QTimer::timeout, [this] {
45  // no locking required at this point
46  // the following calls will acquire the required lock
49  });
50  connect(m_thread, &QThread::finished, timer, &QObject::deleteLater);
51  connect(m_thread, &QThread::started, [timer] { timer->start(); });
52  // move timer to thread so that polling doesn't disturb main event loop
53  timer->moveToThread(m_thread);
54  m_thread->start();
55 
57 }
58 
60 {
62 
63  m_thread->quit();
64  m_thread->wait();
65 }
66 
67 int ClientModel::getNumConnections(unsigned int flags) const
68 {
70 
71  if(flags == CONNECTIONS_IN)
72  connections = CConnman::CONNECTIONS_IN;
73  else if (flags == CONNECTIONS_OUT)
74  connections = CConnman::CONNECTIONS_OUT;
75  else if (flags == CONNECTIONS_ALL)
76  connections = CConnman::CONNECTIONS_ALL;
77 
78  return m_node.getNodeCount(connections);
79 }
80 
82 {
83  if (cachedBestHeaderHeight == -1) {
84  // make sure we initially populate the cache via a cs_main lock
85  // otherwise we need to wait for a tip update
86  int height;
87  int64_t blockTime;
88  if (m_node.getHeaderTip(height, blockTime)) {
89  cachedBestHeaderHeight = height;
90  cachedBestHeaderTime = blockTime;
91  }
92  }
94 }
95 
97 {
98  if (cachedBestHeaderTime == -1) {
99  int height;
100  int64_t blockTime;
101  if (m_node.getHeaderTip(height, blockTime)) {
102  cachedBestHeaderHeight = height;
103  cachedBestHeaderTime = blockTime;
104  }
105  }
106  return cachedBestHeaderTime;
107 }
108 
110 {
111  if (m_cached_num_blocks == -1) {
113  }
114  return m_cached_num_blocks;
115 }
116 
118 {
119  uint256 tip{WITH_LOCK(m_cached_tip_mutex, return m_cached_tip_blocks)};
120 
121  if (!tip.IsNull()) {
122  return tip;
123  }
124 
125  // Lock order must be: first `cs_main`, then `m_cached_tip_mutex`.
126  // The following will lock `cs_main` (and release it), so we must not
127  // own `m_cached_tip_mutex` here.
128  tip = m_node.getBestBlockHash();
129 
131  // We checked that `m_cached_tip_blocks` is not null above, but then we
132  // released the mutex `m_cached_tip_mutex`, so it could have changed in the
133  // meantime. Thus, check again.
134  if (m_cached_tip_blocks.IsNull()) {
135  m_cached_tip_blocks = tip;
136  }
137  return m_cached_tip_blocks;
138 }
139 
140 void ClientModel::updateNumConnections(int numConnections)
141 {
142  Q_EMIT numConnectionsChanged(numConnections);
143 }
144 
145 void ClientModel::updateNetworkActive(bool networkActive)
146 {
147  Q_EMIT networkActiveChanged(networkActive);
148 }
149 
151 {
153 }
154 
156 {
157  if (m_node.getReindex())
158  return BlockSource::REINDEX;
159  else if (m_node.getImporting())
160  return BlockSource::DISK;
161  else if (getNumConnections() > 0)
162  return BlockSource::NETWORK;
163 
164  return BlockSource::NONE;
165 }
166 
168 {
169  return QString::fromStdString(m_node.getWarnings().translated);
170 }
171 
173 {
174  return optionsModel;
175 }
176 
178 {
179  return peerTableModel;
180 }
181 
183 {
184  return banTableModel;
185 }
186 
188 {
189  return QString::fromStdString(FormatFullVersion());
190 }
191 
193 {
194  return QString::fromStdString(strSubVersion);
195 }
196 
198 {
200 }
201 
203 {
204  return QDateTime::fromTime_t(GetStartupTime()).toString();
205 }
206 
207 QString ClientModel::dataDir() const
208 {
210 }
211 
212 QString ClientModel::blocksDir() const
213 {
215 }
216 
218 {
220 }
221 
222 // Handlers for core signals
223 static void ShowProgress(ClientModel *clientmodel, const std::string &title, int nProgress)
224 {
225  // emits signal "showProgress"
226  bool invoked = QMetaObject::invokeMethod(clientmodel, "showProgress", Qt::QueuedConnection,
227  Q_ARG(QString, QString::fromStdString(title)),
228  Q_ARG(int, nProgress));
229  assert(invoked);
230 }
231 
232 static void NotifyNumConnectionsChanged(ClientModel *clientmodel, int newNumConnections)
233 {
234  // Too noisy: qDebug() << "NotifyNumConnectionsChanged: " + QString::number(newNumConnections);
235  bool invoked = QMetaObject::invokeMethod(clientmodel, "updateNumConnections", Qt::QueuedConnection,
236  Q_ARG(int, newNumConnections));
237  assert(invoked);
238 }
239 
240 static void NotifyNetworkActiveChanged(ClientModel *clientmodel, bool networkActive)
241 {
242  bool invoked = QMetaObject::invokeMethod(clientmodel, "updateNetworkActive", Qt::QueuedConnection,
243  Q_ARG(bool, networkActive));
244  assert(invoked);
245 }
246 
247 static void NotifyAlertChanged(ClientModel *clientmodel)
248 {
249  qDebug() << "NotifyAlertChanged";
250  bool invoked = QMetaObject::invokeMethod(clientmodel, "updateAlert", Qt::QueuedConnection);
251  assert(invoked);
252 }
253 
254 static void BannedListChanged(ClientModel *clientmodel)
255 {
256  qDebug() << QString("%1: Requesting update for peer banlist").arg(__func__);
257  bool invoked = QMetaObject::invokeMethod(clientmodel, "updateBanlist", Qt::QueuedConnection);
258  assert(invoked);
259 }
260 
261 static void BlockTipChanged(ClientModel* clientmodel, SynchronizationState sync_state, interfaces::BlockTip tip, double verificationProgress, bool fHeader)
262 {
263  if (fHeader) {
264  // cache best headers time and height to reduce future cs_main locks
265  clientmodel->cachedBestHeaderHeight = tip.block_height;
266  clientmodel->cachedBestHeaderTime = tip.block_time;
267  } else {
268  clientmodel->m_cached_num_blocks = tip.block_height;
269  WITH_LOCK(clientmodel->m_cached_tip_mutex, clientmodel->m_cached_tip_blocks = tip.block_hash;);
270  }
271 
272  // Throttle GUI notifications about (a) blocks during initial sync, and (b) both blocks and headers during reindex.
273  const bool throttle = (sync_state != SynchronizationState::POST_INIT && !fHeader) || sync_state == SynchronizationState::INIT_REINDEX;
274  const int64_t now = throttle ? GetTimeMillis() : 0;
275  int64_t& nLastUpdateNotification = fHeader ? nLastHeaderTipUpdateNotification : nLastBlockTipUpdateNotification;
276  if (throttle && now < nLastUpdateNotification + MODEL_UPDATE_DELAY) {
277  return;
278  }
279 
280  bool invoked = QMetaObject::invokeMethod(clientmodel, "numBlocksChanged", Qt::QueuedConnection,
281  Q_ARG(int, tip.block_height),
282  Q_ARG(QDateTime, QDateTime::fromTime_t(tip.block_time)),
283  Q_ARG(double, verificationProgress),
284  Q_ARG(bool, fHeader),
285  Q_ARG(SynchronizationState, sync_state));
286  assert(invoked);
287  nLastUpdateNotification = now;
288 }
289 
291 {
292  // Connect signals to client
293  m_handler_show_progress = m_node.handleShowProgress(std::bind(ShowProgress, this, std::placeholders::_1, std::placeholders::_2));
298  m_handler_notify_block_tip = m_node.handleNotifyBlockTip(std::bind(BlockTipChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, false));
299  m_handler_notify_header_tip = m_node.handleNotifyHeaderTip(std::bind(BlockTipChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, true));
300 }
301 
303 {
304  // Disconnect signals from client
305  m_handler_show_progress->disconnect();
308  m_handler_notify_alert_changed->disconnect();
309  m_handler_banned_list_changed->disconnect();
310  m_handler_notify_block_tip->disconnect();
311  m_handler_notify_header_tip->disconnect();
312 }
313 
314 bool ClientModel::getProxyInfo(std::string& ip_port) const
315 {
316  proxyType ipv4, ipv6;
317  if (m_node.getProxy((Network) 1, ipv4) && m_node.getProxy((Network) 2, ipv6)) {
318  ip_port = ipv4.proxy.ToStringIPPort();
319  return true;
320  }
321  return false;
322 }
virtual std::unique_ptr< Handler > handleNotifyHeaderTip(NotifyHeaderTipFn fn)=0
QString formatClientStartupTime() const
PeerTableModel * peerTableModel
Definition: clientmodel.h:98
void updateNetworkActive(bool networkActive)
QString formatSubVersion() const
int64_t block_time
Definition: node.h:276
static void NotifyAlertChanged(ClientModel *clientmodel)
Block tip (could be a header or not, depends on the subscribed signal).
Definition: node.h:274
interfaces::Node & m_node
Definition: clientmodel.h:86
SynchronizationState
Current sync state passed to tip changed callbacks.
Definition: validation.h:109
bool isReleaseVersion() const
std::atomic< int64_t > cachedBestHeaderTime
Definition: clientmodel.h:82
void updateBanlist()
int64_t GetTimeMillis()
Returns the system time (not mockable)
Definition: time.cpp:57
QString blocksDir() const
virtual size_t getMempoolDynamicUsage()=0
Get mempool dynamic usage.
virtual bool getProxy(Network net, proxyType &proxy_info)=0
Get proxy.
QThread *const m_thread
A thread to interact with m_node asynchronously.
Definition: clientmodel.h:102
NodeContext & m_node
Definition: chain.cpp:382
virtual int64_t getTotalBytesRecv()=0
Get total bytes recv.
virtual bool getImporting()=0
Get importing.
virtual size_t getMempoolSize()=0
Get mempool size.
static void ShowProgress(ClientModel *clientmodel, const std::string &title, int nProgress)
void networkActiveChanged(bool networkActive)
BanTableModel * banTableModel
Definition: clientmodel.h:99
ClientModel(interfaces::Node &node, OptionsModel *optionsModel, QObject *parent=nullptr)
Definition: clientmodel.cpp:29
std::unique_ptr< interfaces::Handler > m_handler_show_progress
Definition: clientmodel.h:90
OptionsModel * getOptionsModel()
Mutex m_cached_tip_mutex
Definition: clientmodel.h:85
const fs::path & GetBlocksDir()
Definition: system.cpp:595
std::string translated
Definition: translation.h:18
std::atomic< int > cachedBestHeaderHeight
Definition: clientmodel.h:81
PeerTableModel * getPeerTableModel()
virtual std::unique_ptr< Handler > handleNotifyBlockTip(NotifyBlockTipFn fn)=0
void numConnectionsChanged(int count)
QString getStatusBarWarnings() const
Return warnings to be displayed in status bar.
void alertsChanged(const QString &warnings)
std::unique_ptr< interfaces::Handler > m_handler_notify_block_tip
Definition: clientmodel.h:95
virtual std::unique_ptr< Handler > handleNotifyNumConnectionsChanged(NotifyNumConnectionsChangedFn fn)=0
void bytesChanged(quint64 totalBytesIn, quint64 totalBytesOut)
#define CLIENT_VERSION_IS_RELEASE
static int64_t nLastHeaderTipUpdateNotification
Definition: clientmodel.cpp:26
std::atomic< int > m_cached_num_blocks
Definition: clientmodel.h:83
int getNumConnections(unsigned int flags=CONNECTIONS_ALL) const
Return number of connections, default is in- and outbound (total)
Definition: clientmodel.cpp:67
enum BlockSource getBlockSource() const
Returns enum BlockSource of the current importing/syncing state.
Qt model providing information about connected peers, similar to the "getpeerinfo" RPC call...
#define LOCK(cs)
Definition: sync.h:226
BlockSource
Definition: clientmodel.h:31
virtual bilingual_str getWarnings()=0
Get warnings.
const fs::path & GetDataDir(bool fNetSpecific)
Definition: system.cpp:620
void unsubscribeFromCoreSignals()
virtual std::unique_ptr< Handler > handleShowProgress(ShowProgressFn fn)=0
BanTableModel * getBanTableModel()
void subscribeToCoreSignals()
Network
Definition: netaddress.h:19
std::unique_ptr< interfaces::Handler > m_handler_banned_list_changed
Definition: clientmodel.h:94
NumConnections
Definition: net.h:122
virtual std::unique_ptr< Handler > handleBannedListChanged(BannedListChangedFn fn)=0
std::string strSubVersion
Subversion as sent to the P2P network in version messages.
Definition: net.cpp:106
Model for Bitcoin network client.
Definition: clientmodel.h:46
std::unique_ptr< interfaces::Handler > m_handler_notify_header_tip
Definition: clientmodel.h:96
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Definition: sync.h:253
std::string FormatFullVersion()
static const int MODEL_UPDATE_DELAY
Definition: guiconstants.h:11
int flags
Definition: bitcoin-tx.cpp:509
static void BannedListChanged(ClientModel *clientmodel)
virtual bool getReindex()=0
Get reindex.
256-bit opaque blob.
Definition: uint256.h:120
void updateAlert()
std::unique_ptr< interfaces::Handler > m_handler_notify_num_connections_changed
Definition: clientmodel.h:91
CService proxy
Definition: netbase.h:36
int64_t GetStartupTime()
Server/client environment: argument handling, config file parsing, thread wrappers, startup time.
Definition: system.cpp:1149
virtual uint256 getBestBlockHash()=0
Get best block hash.
Interface from Qt to configuration data structure for Bitcoin client.
Definition: optionsmodel.h:37
static int64_t nLastBlockTipUpdateNotification
Definition: clientmodel.cpp:27
Qt model providing information about connected peers, similar to the "getpeerinfo" RPC call...
Definition: bantablemodel.h:42
std::string ToStringIPPort() const
Definition: netaddress.cpp:742
virtual bool getHeaderTip(int &height, int64_t &block_time)=0
Get header tip height and time.
static void NotifyNetworkActiveChanged(ClientModel *clientmodel, bool networkActive)
OptionsModel * optionsModel
Definition: clientmodel.h:97
static void BlockTipChanged(ClientModel *clientmodel, SynchronizationState sync_state, interfaces::BlockTip tip, double verificationProgress, bool fHeader)
void mempoolSizeChanged(long count, size_t mempoolSizeInBytes)
QString dataDir() const
uint256 getBestBlockHash()
virtual int getNumBlocks()=0
Get num blocks.
virtual std::unique_ptr< Handler > handleNotifyAlertChanged(NotifyAlertChangedFn fn)=0
std::unique_ptr< interfaces::Handler > m_handler_notify_network_active_changed
Definition: clientmodel.h:92
int getNumBlocks() const
void updateNumConnections(int numConnections)
int getHeaderTipHeight() const
Definition: clientmodel.cpp:81
QString boostPathToQString(const fs::path &path)
Definition: guiutil.cpp:729
std::unique_ptr< interfaces::Handler > m_handler_notify_alert_changed
Definition: clientmodel.h:93
virtual std::unique_ptr< Handler > handleNotifyNetworkActiveChanged(NotifyNetworkActiveChangedFn fn)=0
static void NotifyNumConnectionsChanged(ClientModel *clientmodel, int newNumConnections)
virtual int64_t getTotalBytesSent()=0
Get total bytes sent.
int64_t getHeaderTipTime() const
Definition: clientmodel.cpp:96
Top-level interface for a bitcoin node (bitcoind process).
Definition: node.h:43
uint256 block_hash
Definition: node.h:277
virtual size_t getNodeCount(CConnman::NumConnections flags)=0
Get number of connections.
QString formatFullVersion() const
bool getProxyInfo(std::string &ip_port) const