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