Bitcoin Core  28.1.0
P2P Digital Currency
clientmodel.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-2022 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 <config/bitcoin-config.h> // IWYU pragma: keep
6 
7 #include <qt/clientmodel.h>
8 
9 #include <qt/bantablemodel.h>
10 #include <qt/guiconstants.h>
11 #include <qt/guiutil.h>
12 #include <qt/peertablemodel.h>
13 #include <qt/peertablesortproxy.h>
14 
15 #include <clientversion.h>
16 #include <common/args.h>
17 #include <common/system.h>
18 #include <interfaces/handler.h>
19 #include <interfaces/node.h>
20 #include <net.h>
21 #include <netbase.h>
22 #include <util/threadnames.h>
23 #include <util/time.h>
24 #include <validation.h>
25 
26 #include <stdint.h>
27 
28 #include <QDebug>
29 #include <QMetaObject>
30 #include <QThread>
31 #include <QTimer>
32 
33 static SteadyClock::time_point g_last_header_tip_update_notification{};
34 static SteadyClock::time_point g_last_block_tip_update_notification{};
35 
36 ClientModel::ClientModel(interfaces::Node& node, OptionsModel *_optionsModel, QObject *parent) :
37  QObject(parent),
38  m_node(node),
39  optionsModel(_optionsModel),
40  m_thread(new QThread(this))
41 {
44 
47  m_peer_table_sort_proxy->setSourceModel(peerTableModel);
48 
49  banTableModel = new BanTableModel(m_node, this);
50 
51  QTimer* timer = new QTimer;
52  timer->setInterval(MODEL_UPDATE_DELAY);
53  connect(timer, &QTimer::timeout, [this] {
54  // no locking required at this point
55  // the following calls will acquire the required lock
58  });
59  connect(m_thread, &QThread::finished, timer, &QObject::deleteLater);
60  connect(m_thread, &QThread::started, [timer] { timer->start(); });
61  // move timer to thread so that polling doesn't disturb main event loop
62  timer->moveToThread(m_thread);
63  m_thread->start();
64  QTimer::singleShot(0, timer, []() {
65  util::ThreadRename("qt-clientmodl");
66  });
67 
69 }
70 
72 {
74 
75  m_thread->quit();
76  m_thread->wait();
77 }
78 
80 {
81  stop();
82 }
83 
84 int ClientModel::getNumConnections(unsigned int flags) const
85 {
87 
88  if(flags == CONNECTIONS_IN)
89  connections = ConnectionDirection::In;
90  else if (flags == CONNECTIONS_OUT)
91  connections = ConnectionDirection::Out;
92  else if (flags == CONNECTIONS_ALL)
93  connections = ConnectionDirection::Both;
94 
95  return m_node.getNodeCount(connections);
96 }
97 
99 {
100  if (cachedBestHeaderHeight == -1) {
101  // make sure we initially populate the cache via a cs_main lock
102  // otherwise we need to wait for a tip update
103  int height;
104  int64_t blockTime;
105  if (m_node.getHeaderTip(height, blockTime)) {
106  cachedBestHeaderHeight = height;
107  cachedBestHeaderTime = blockTime;
108  }
109  }
110  return cachedBestHeaderHeight;
111 }
112 
114 {
115  if (cachedBestHeaderTime == -1) {
116  int height;
117  int64_t blockTime;
118  if (m_node.getHeaderTip(height, blockTime)) {
119  cachedBestHeaderHeight = height;
120  cachedBestHeaderTime = blockTime;
121  }
122  }
123  return cachedBestHeaderTime;
124 }
125 
126 
127 std::map<CNetAddr, LocalServiceInfo> ClientModel::getNetLocalAddresses() const
128 {
129  return m_node.getNetLocalAddresses();
130 }
131 
132 
134 {
135  if (m_cached_num_blocks == -1) {
137  }
138  return m_cached_num_blocks;
139 }
140 
142 {
143  uint256 tip{WITH_LOCK(m_cached_tip_mutex, return m_cached_tip_blocks)};
144 
145  if (!tip.IsNull()) {
146  return tip;
147  }
148 
149  // Lock order must be: first `cs_main`, then `m_cached_tip_mutex`.
150  // The following will lock `cs_main` (and release it), so we must not
151  // own `m_cached_tip_mutex` here.
152  tip = m_node.getBestBlockHash();
153 
155  // We checked that `m_cached_tip_blocks` is not null above, but then we
156  // released the mutex `m_cached_tip_mutex`, so it could have changed in the
157  // meantime. Thus, check again.
158  if (m_cached_tip_blocks.IsNull()) {
159  m_cached_tip_blocks = tip;
160  }
161  return m_cached_tip_blocks;
162 }
163 
165 {
167  if (getNumConnections() > 0) return BlockSource::NETWORK;
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 {
189 }
190 
192 {
193  return banTableModel;
194 }
195 
197 {
198  return QString::fromStdString(FormatFullVersion());
199 }
200 
202 {
203  return QString::fromStdString(strSubVersion);
204 }
205 
207 {
209 }
210 
212 {
213  return QDateTime::fromSecsSinceEpoch(GetStartupTime()).toString();
214 }
215 
216 QString ClientModel::dataDir() const
217 {
219 }
220 
221 QString ClientModel::blocksDir() const
222 {
224 }
225 
226 void ClientModel::TipChanged(SynchronizationState sync_state, interfaces::BlockTip tip, double verification_progress, SyncType synctype)
227 {
228  if (synctype == SyncType::HEADER_SYNC) {
229  // cache best headers time and height to reduce future cs_main locks
232  } else if (synctype == SyncType::BLOCK_SYNC) {
234  WITH_LOCK(m_cached_tip_mutex, m_cached_tip_blocks = tip.block_hash;);
235  }
236 
237  // Throttle GUI notifications about (a) blocks during initial sync, and (b) both blocks and headers during reindex.
238  const bool throttle = (sync_state != SynchronizationState::POST_INIT && synctype == SyncType::BLOCK_SYNC) || sync_state == SynchronizationState::INIT_REINDEX;
239  const auto now{throttle ? SteadyClock::now() : SteadyClock::time_point{}};
241  if (throttle && now < nLastUpdateNotification + MODEL_UPDATE_DELAY) {
242  return;
243  }
244 
245  Q_EMIT numBlocksChanged(tip.block_height, QDateTime::fromSecsSinceEpoch(tip.block_time), verification_progress, synctype, sync_state);
246  nLastUpdateNotification = now;
247 }
248 
250 {
252  [this](const std::string& title, int progress, [[maybe_unused]] bool resume_possible) {
253  Q_EMIT showProgress(QString::fromStdString(title), progress);
254  }));
256  [this](int new_num_connections) {
257  Q_EMIT numConnectionsChanged(new_num_connections);
258  }));
260  [this](bool network_active) {
261  Q_EMIT networkActiveChanged(network_active);
262  }));
264  [this]() {
265  qDebug() << "ClientModel: NotifyAlertChanged";
266  Q_EMIT alertsChanged(getStatusBarWarnings());
267  }));
269  [this]() {
270  qDebug() << "ClienModel: Requesting update for peer banlist";
271  QMetaObject::invokeMethod(banTableModel, [this] { banTableModel->refresh(); });
272  }));
273  m_event_handlers.emplace_back(m_node.handleNotifyBlockTip(
274  [this](SynchronizationState sync_state, interfaces::BlockTip tip, double verification_progress) {
275  TipChanged(sync_state, tip, verification_progress, SyncType::BLOCK_SYNC);
276  }));
277  m_event_handlers.emplace_back(m_node.handleNotifyHeaderTip(
278  [this](SynchronizationState sync_state, interfaces::BlockTip tip, bool presync) {
279  TipChanged(sync_state, tip, /*verification_progress=*/0.0, presync ? SyncType::HEADER_PRESYNC : SyncType::HEADER_SYNC);
280  }));
281 }
282 
284 {
285  m_event_handlers.clear();
286 }
287 
288 bool ClientModel::getProxyInfo(std::string& ip_port) const
289 {
290  Proxy ipv4, ipv6;
291  if (m_node.getProxy((Network) 1, ipv4) && m_node.getProxy((Network) 2, ipv6)) {
292  ip_port = ipv4.proxy.ToStringAddrPort();
293  return true;
294  }
295  return false;
296 }
virtual size_t getMempoolMaxUsage()=0
Get mempool maximum memory usage.
QString formatClientStartupTime() const
PeerTableModel * peerTableModel
Definition: clientmodel.h:106
QString formatSubVersion() const
int64_t block_time
Definition: node.h:284
Block tip (could be a header or not, depends on the subscribed signal).
Definition: node.h:282
interfaces::Node & m_node
Definition: clientmodel.h:100
SynchronizationState
Current sync state passed to tip changed callbacks.
Definition: validation.h:82
std::vector< std::unique_ptr< interfaces::Handler > > m_event_handlers
Definition: clientmodel.h:104
bool isReleaseVersion() const
std::atomic< int64_t > cachedBestHeaderTime
Definition: clientmodel.h:96
static SteadyClock::time_point g_last_header_tip_update_notification
Definition: clientmodel.cpp:33
QString blocksDir() const
virtual size_t getMempoolDynamicUsage()=0
Get mempool dynamic usage.
node::NodeContext m_node
Definition: bitcoin-gui.cpp:37
QThread *const m_thread
A thread to interact with m_node asynchronously.
Definition: clientmodel.h:111
virtual int64_t getTotalBytesRecv()=0
Get total bytes recv.
virtual size_t getMempoolSize()=0
Get mempool size.
BanTableModel * banTableModel
Definition: clientmodel.h:108
static constexpr auto MODEL_UPDATE_DELAY
Definition: guiconstants.h:14
ClientModel(interfaces::Node &node, OptionsModel *optionsModel, QObject *parent=nullptr)
Definition: clientmodel.cpp:36
OptionsModel * getOptionsModel()
Mutex m_cached_tip_mutex
Definition: clientmodel.h:99
std::string translated
Definition: translation.h:20
std::atomic< int > cachedBestHeaderHeight
Definition: clientmodel.h:95
PeerTableModel * getPeerTableModel()
void ThreadRename(const 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
static SteadyClock::time_point g_last_block_tip_update_notification
Definition: clientmodel.cpp:34
virtual size_t getNodeCount(ConnectionDirection flags)=0
Get number of connections.
std::string ToStringAddrPort() const
Definition: netaddress.cpp:905
QString getStatusBarWarnings() const
Return warnings to be displayed in status bar.
virtual std::unique_ptr< Handler > handleNotifyNumConnectionsChanged(NotifyNumConnectionsChangedFn fn)=0
void bytesChanged(quint64 totalBytesIn, quint64 totalBytesOut)
uint256 getBestBlockHash() EXCLUSIVE_LOCKS_REQUIRED(!m_cached_tip_mutex)
#define CLIENT_VERSION_IS_RELEASE
CService proxy
Definition: netbase.h:65
BlockSource getBlockSource() const
Returns the block source of the current importing/syncing state.
std::atomic< int > m_cached_num_blocks
Definition: clientmodel.h:97
fs::path GetDataDirNet() const
Get data directory path with appended network identifier.
Definition: args.h:232
void mempoolSizeChanged(long count, size_t mempoolSizeInBytes, size_t mempoolMaxSizeInBytes)
int getNumConnections(unsigned int flags=CONNECTIONS_ALL) const
Return number of connections, default is in- and outbound (total)
Definition: clientmodel.cpp:84
std::map< CNetAddr, LocalServiceInfo > getNetLocalAddresses() const
PeerTableSortProxy * m_peer_table_sort_proxy
Definition: clientmodel.h:107
Qt model providing information about connected peers, similar to the "getpeerinfo" RPC call...
virtual bool isLoadingBlocks()=0
Is loading blocks.
#define LOCK(cs)
Definition: sync.h:257
BlockSource
Definition: clientmodel.h:36
fs::path GetBlocksDirPath() const
Get blocks directory path.
Definition: args.cpp:281
virtual bilingual_str getWarnings()=0
Get warnings.
void unsubscribeFromCoreSignals()
virtual std::unique_ptr< Handler > handleShowProgress(ShowProgressFn fn)=0
void numBlocksChanged(int count, const QDateTime &blockDate, double nVerificationProgress, SyncType header, SynchronizationState sync_state)
BanTableModel * getBanTableModel()
virtual bool getProxy(Network net, Proxy &proxy_info)=0
Get proxy.
void subscribeToCoreSignals()
Network
A network type.
Definition: netaddress.h:32
void TipChanged(SynchronizationState sync_state, interfaces::BlockTip tip, double verification_progress, SyncType synctype) EXCLUSIVE_LOCKS_REQUIRED(!m_cached_tip_mutex)
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:117
virtual std::map< CNetAddr, LocalServiceInfo > getNetLocalAddresses()=0
Get network local addresses.
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Definition: sync.h:301
std::string FormatFullVersion()
SyncType
Definition: clientmodel.h:42
Definition: messages.h:20
ArgsManager gArgs
Definition: args.cpp:41
Definition: netbase.h:58
int flags
Definition: bitcoin-tx.cpp:533
256-bit opaque blob.
Definition: uint256.h:178
virtual uint256 getBestBlockHash()=0
Get best block hash.
Interface from Qt to configuration data structure for Bitcoin client.
Definition: optionsmodel.h:42
Qt model providing information about banned peers, similar to the "getpeerinfo" RPC call...
Definition: bantablemodel.h:43
virtual bool getHeaderTip(int &height, int64_t &block_time)=0
Get header tip height and time.
OptionsModel * optionsModel
Definition: clientmodel.h:105
int64_t GetStartupTime()
Definition: system.cpp:109
QString dataDir() const
virtual int getNumBlocks()=0
Get num blocks.
virtual std::unique_ptr< Handler > handleNotifyAlertChanged(NotifyAlertChangedFn fn)=0
int getNumBlocks() const
PeerTableSortProxy * peerTableSortProxy()
int getHeaderTipHeight() const
Definition: clientmodel.cpp:98
virtual std::unique_ptr< Handler > handleNotifyNetworkActiveChanged(NotifyNetworkActiveChangedFn fn)=0
ConnectionDirection
Definition: netbase.h:33
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:69
uint256 block_hash
Definition: node.h:285
QString PathToQString(const fs::path &path)
Convert OS specific boost path to QString through UTF-8.
Definition: guiutil.cpp:682
QString formatFullVersion() const
bool getProxyInfo(std::string &ip_port) const