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 <util/threadnames.h>
19 #include <validation.h>
20 
21 #include <stdint.h>
22 
23 #include <QDebug>
24 #include <QThread>
25 #include <QTimer>
26 
29 
30 ClientModel::ClientModel(interfaces::Node& node, OptionsModel *_optionsModel, QObject *parent) :
31  QObject(parent),
32  m_node(node),
33  optionsModel(_optionsModel),
34  peerTableModel(nullptr),
35  banTableModel(nullptr),
36  m_thread(new QThread(this))
37 {
41  banTableModel = new BanTableModel(m_node, this);
42 
43  QTimer* timer = new QTimer;
44  timer->setInterval(MODEL_UPDATE_DELAY);
45  connect(timer, &QTimer::timeout, [this] {
46  // no locking required at this point
47  // the following calls will acquire the required lock
50  });
51  connect(m_thread, &QThread::finished, timer, &QObject::deleteLater);
52  connect(m_thread, &QThread::started, [timer] { timer->start(); });
53  // move timer to thread so that polling doesn't disturb main event loop
54  timer->moveToThread(m_thread);
55  m_thread->start();
56  QTimer::singleShot(0, timer, []() {
57  util::ThreadRename("qt-clientmodl");
58  });
59 
61 }
62 
64 {
66 
67  m_thread->quit();
68  m_thread->wait();
69 }
70 
71 int ClientModel::getNumConnections(unsigned int flags) const
72 {
74 
75  if(flags == CONNECTIONS_IN)
76  connections = CConnman::CONNECTIONS_IN;
77  else if (flags == CONNECTIONS_OUT)
78  connections = CConnman::CONNECTIONS_OUT;
79  else if (flags == CONNECTIONS_ALL)
80  connections = CConnman::CONNECTIONS_ALL;
81 
82  return m_node.getNodeCount(connections);
83 }
84 
86 {
87  if (cachedBestHeaderHeight == -1) {
88  // make sure we initially populate the cache via a cs_main lock
89  // otherwise we need to wait for a tip update
90  int height;
91  int64_t blockTime;
92  if (m_node.getHeaderTip(height, blockTime)) {
93  cachedBestHeaderHeight = height;
94  cachedBestHeaderTime = blockTime;
95  }
96  }
98 }
99 
101 {
102  if (cachedBestHeaderTime == -1) {
103  int height;
104  int64_t blockTime;
105  if (m_node.getHeaderTip(height, blockTime)) {
106  cachedBestHeaderHeight = height;
107  cachedBestHeaderTime = blockTime;
108  }
109  }
110  return cachedBestHeaderTime;
111 }
112 
114 {
115  if (m_cached_num_blocks == -1) {
117  }
118  return m_cached_num_blocks;
119 }
120 
122 {
123  uint256 tip{WITH_LOCK(m_cached_tip_mutex, return m_cached_tip_blocks)};
124 
125  if (!tip.IsNull()) {
126  return tip;
127  }
128 
129  // Lock order must be: first `cs_main`, then `m_cached_tip_mutex`.
130  // The following will lock `cs_main` (and release it), so we must not
131  // own `m_cached_tip_mutex` here.
132  tip = m_node.getBestBlockHash();
133 
135  // We checked that `m_cached_tip_blocks` is not null above, but then we
136  // released the mutex `m_cached_tip_mutex`, so it could have changed in the
137  // meantime. Thus, check again.
138  if (m_cached_tip_blocks.IsNull()) {
139  m_cached_tip_blocks = tip;
140  }
141  return m_cached_tip_blocks;
142 }
143 
144 void ClientModel::updateNumConnections(int numConnections)
145 {
146  Q_EMIT numConnectionsChanged(numConnections);
147 }
148 
149 void ClientModel::updateNetworkActive(bool networkActive)
150 {
151  Q_EMIT networkActiveChanged(networkActive);
152 }
153 
155 {
157 }
158 
160 {
161  if (m_node.getReindex())
162  return BlockSource::REINDEX;
163  else if (m_node.getImporting())
164  return BlockSource::DISK;
165  else if (getNumConnections() > 0)
166  return BlockSource::NETWORK;
167 
168  return BlockSource::NONE;
169 }
170 
172 {
173  return QString::fromStdString(m_node.getWarnings().translated);
174 }
175 
177 {
178  return optionsModel;
179 }
180 
182 {
183  return peerTableModel;
184 }
185 
187 {
188  return banTableModel;
189 }
190 
192 {
193  return QString::fromStdString(FormatFullVersion());
194 }
195 
197 {
198  return QString::fromStdString(strSubVersion);
199 }
200 
202 {
204 }
205 
207 {
208  return QDateTime::fromTime_t(GetStartupTime()).toString();
209 }
210 
211 QString ClientModel::dataDir() const
212 {
214 }
215 
216 QString ClientModel::blocksDir() const
217 {
219 }
220 
222 {
224 }
225 
226 // Handlers for core signals
227 static void ShowProgress(ClientModel *clientmodel, const std::string &title, int nProgress)
228 {
229  // emits signal "showProgress"
230  bool invoked = QMetaObject::invokeMethod(clientmodel, "showProgress", Qt::QueuedConnection,
231  Q_ARG(QString, QString::fromStdString(title)),
232  Q_ARG(int, nProgress));
233  assert(invoked);
234 }
235 
236 static void NotifyNumConnectionsChanged(ClientModel *clientmodel, int newNumConnections)
237 {
238  // Too noisy: qDebug() << "NotifyNumConnectionsChanged: " + QString::number(newNumConnections);
239  bool invoked = QMetaObject::invokeMethod(clientmodel, "updateNumConnections", Qt::QueuedConnection,
240  Q_ARG(int, newNumConnections));
241  assert(invoked);
242 }
243 
244 static void NotifyNetworkActiveChanged(ClientModel *clientmodel, bool networkActive)
245 {
246  bool invoked = QMetaObject::invokeMethod(clientmodel, "updateNetworkActive", Qt::QueuedConnection,
247  Q_ARG(bool, networkActive));
248  assert(invoked);
249 }
250 
251 static void NotifyAlertChanged(ClientModel *clientmodel)
252 {
253  qDebug() << "NotifyAlertChanged";
254  bool invoked = QMetaObject::invokeMethod(clientmodel, "updateAlert", Qt::QueuedConnection);
255  assert(invoked);
256 }
257 
258 static void BannedListChanged(ClientModel *clientmodel)
259 {
260  qDebug() << QString("%1: Requesting update for peer banlist").arg(__func__);
261  bool invoked = QMetaObject::invokeMethod(clientmodel, "updateBanlist", Qt::QueuedConnection);
262  assert(invoked);
263 }
264 
265 static void BlockTipChanged(ClientModel* clientmodel, SynchronizationState sync_state, interfaces::BlockTip tip, double verificationProgress, bool fHeader)
266 {
267  if (fHeader) {
268  // cache best headers time and height to reduce future cs_main locks
269  clientmodel->cachedBestHeaderHeight = tip.block_height;
270  clientmodel->cachedBestHeaderTime = tip.block_time;
271  } else {
272  clientmodel->m_cached_num_blocks = tip.block_height;
273  WITH_LOCK(clientmodel->m_cached_tip_mutex, clientmodel->m_cached_tip_blocks = tip.block_hash;);
274  }
275 
276  // Throttle GUI notifications about (a) blocks during initial sync, and (b) both blocks and headers during reindex.
277  const bool throttle = (sync_state != SynchronizationState::POST_INIT && !fHeader) || sync_state == SynchronizationState::INIT_REINDEX;
278  const int64_t now = throttle ? GetTimeMillis() : 0;
279  int64_t& nLastUpdateNotification = fHeader ? nLastHeaderTipUpdateNotification : nLastBlockTipUpdateNotification;
280  if (throttle && now < nLastUpdateNotification + MODEL_UPDATE_DELAY) {
281  return;
282  }
283 
284  bool invoked = QMetaObject::invokeMethod(clientmodel, "numBlocksChanged", Qt::QueuedConnection,
285  Q_ARG(int, tip.block_height),
286  Q_ARG(QDateTime, QDateTime::fromTime_t(tip.block_time)),
287  Q_ARG(double, verificationProgress),
288  Q_ARG(bool, fHeader),
289  Q_ARG(SynchronizationState, sync_state));
290  assert(invoked);
291  nLastUpdateNotification = now;
292 }
293 
295 {
296  // Connect signals to client
297  m_handler_show_progress = m_node.handleShowProgress(std::bind(ShowProgress, this, std::placeholders::_1, std::placeholders::_2));
302  m_handler_notify_block_tip = m_node.handleNotifyBlockTip(std::bind(BlockTipChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, false));
303  m_handler_notify_header_tip = m_node.handleNotifyHeaderTip(std::bind(BlockTipChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, true));
304 }
305 
307 {
308  // Disconnect signals from client
309  m_handler_show_progress->disconnect();
312  m_handler_notify_alert_changed->disconnect();
313  m_handler_banned_list_changed->disconnect();
314  m_handler_notify_block_tip->disconnect();
315  m_handler_notify_header_tip->disconnect();
316 }
317 
318 bool ClientModel::getProxyInfo(std::string& ip_port) const
319 {
320  proxyType ipv4, ipv6;
321  if (m_node.getProxy((Network) 1, ipv4) && m_node.getProxy((Network) 2, ipv6)) {
322  ip_port = ipv4.proxy.ToStringIPPort();
323  return true;
324  }
325  return false;
326 }
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:236
static void NotifyAlertChanged(ClientModel *clientmodel)
Block tip (could be a header or not, depends on the subscribed signal).
Definition: node.h:234
interfaces::Node & m_node
Definition: clientmodel.h:86
const std::chrono::seconds now
SynchronizationState
Current sync state passed to tip changed callbacks.
Definition: validation.h:106
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:411
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 ThreadRename(std::string &&)
Rename a thread both in terms of an internal (in-memory) name as well as its system thread name...
Definition: threadnames.cpp:57
void networkActiveChanged(bool networkActive)
BanTableModel * banTableModel
Definition: clientmodel.h:99
ClientModel(interfaces::Node &node, OptionsModel *optionsModel, QObject *parent=nullptr)
Definition: clientmodel.cpp:30
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:681
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:27
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:71
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:230
BlockSource
Definition: clientmodel.h:31
virtual bilingual_str getWarnings()=0
Get warnings.
const fs::path & GetDataDir(bool fNetSpecific)
Definition: system.cpp:706
void unsubscribeFromCoreSignals()
virtual std::unique_ptr< Handler > handleShowProgress(ShowProgressFn fn)=0
BanTableModel * getBanTableModel()
void subscribeToCoreSignals()
Network
A network type.
Definition: netaddress.h:43
std::unique_ptr< interfaces::Handler > m_handler_banned_list_changed
Definition: clientmodel.h:94
NumConnections
Definition: net.h:191
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:118
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:257
std::string FormatFullVersion()
static const int MODEL_UPDATE_DELAY
Definition: guiconstants.h:11
int flags
Definition: bitcoin-tx.cpp:506
static void BannedListChanged(ClientModel *clientmodel)
virtual bool getReindex()=0
Get reindex.
256-bit opaque blob.
Definition: uint256.h:124
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()
Definition: system.cpp:1280
virtual uint256 getBestBlockHash()=0
Get best block hash.
Interface from Qt to configuration data structure for Bitcoin client.
Definition: optionsmodel.h:39
static int64_t nLastBlockTipUpdateNotification
Definition: clientmodel.cpp:28
Qt model providing information about connected peers, similar to the "getpeerinfo" RPC call...
Definition: bantablemodel.h:42
std::string ToStringIPPort() const
Definition: netaddress.cpp:968
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:85
QString boostPathToQString(const fs::path &path)
Definition: guiutil.cpp:746
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
Top-level interface for a bitcoin node (bitcoind process).
Definition: node.h:52
uint256 block_hash
Definition: node.h:237
virtual size_t getNodeCount(CConnman::NumConnections flags)=0
Get number of connections.
QString formatFullVersion() const
bool getProxyInfo(std::string &ip_port) const