Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1// Copyright (c) 2011-present The Bitcoin Core developers2// Distributed under the MIT software license, see the accompanying3// file COPYING or http://www.opensource.org/licenses/mit-license.php.5#include <bitcoin-build-config.h> // IWYU pragma: keep7#include <qt/clientmodel.h>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>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>26#include <cstdint>28#include <QDebug>29#include <QMetaObject>30#include <QThread>31#include <QTimer>33static SteadyClock::time_point g_last_header_tip_update_notification{};34static SteadyClock::time_point g_last_block_tip_update_notification{};36ClientModel::ClientModel(interfaces::Node& node, OptionsModel *_optionsModel, QObject *parent) :37QObject(parent),38m_node(node),39optionsModel(_optionsModel),40m_thread(new QThread(this))41{42cachedBestHeaderHeight = -1;43cachedBestHeaderTime = -1;45peerTableModel = new PeerTableModel(m_node, this);46m_peer_table_sort_proxy = new PeerTableSortProxy(this);47m_peer_table_sort_proxy->setSourceModel(peerTableModel);49banTableModel = new BanTableModel(m_node, this);51QTimer* timer = new QTimer;52timer->setInterval(MODEL_UPDATE_DELAY);53connect(timer, &QTimer::timeout, [this] {54// no locking required at this point55// the following calls will acquire the required lock56Q_EMIT mempoolSizeChanged(m_node.getMempoolSize(), m_node.getMempoolDynamicUsage(), m_node.getMempoolMaxUsage());57Q_EMIT bytesChanged(m_node.getTotalBytesRecv(), m_node.getTotalBytesSent());58});59connect(m_thread, &QThread::finished, timer, &QObject::deleteLater);60connect(m_thread, &QThread::started, [timer] { timer->start(); });61// move timer to thread so that polling doesn't disturb main event loop62timer->moveToThread(m_thread);63m_thread->start();64QTimer::singleShot(0, timer, []() {65util::ThreadRename("qt-clientmodl");66});68subscribeToCoreSignals();69}71void ClientModel::stop()72{73unsubscribeFromCoreSignals();75m_thread->quit();76m_thread->wait();77}79ClientModel::~ClientModel()80{81stop();82}84int ClientModel::getNumConnections(unsigned int flags) const85{86ConnectionDirection connections = ConnectionDirection::None;88if(flags == CONNECTIONS_IN)89connections = ConnectionDirection::In;90else if (flags == CONNECTIONS_OUT)91connections = ConnectionDirection::Out;92else if (flags == CONNECTIONS_ALL)93connections = ConnectionDirection::Both;95return m_node.getNodeCount(connections);96}98int ClientModel::getHeaderTipHeight() const99{100if (cachedBestHeaderHeight == -1) {101// make sure we initially populate the cache via a cs_main lock102// otherwise we need to wait for a tip update103int height;104int64_t blockTime;105if (m_node.getHeaderTip(height, blockTime)) {106cachedBestHeaderHeight = height;107cachedBestHeaderTime = blockTime;108}109}110return cachedBestHeaderHeight;111}113int64_t ClientModel::getHeaderTipTime() const114{115if (cachedBestHeaderTime == -1) {116int height;117int64_t blockTime;118if (m_node.getHeaderTip(height, blockTime)) {119cachedBestHeaderHeight = height;120cachedBestHeaderTime = blockTime;121}122}123return cachedBestHeaderTime;124}127std::map<CNetAddr, LocalServiceInfo> ClientModel::getNetLocalAddresses() const128{129return m_node.getNetLocalAddresses();130}133int ClientModel::getNumBlocks() const134{135if (m_cached_num_blocks == -1) {136m_cached_num_blocks = m_node.getNumBlocks();137}138return m_cached_num_blocks;139}141uint256 ClientModel::getBestBlockHash()142{143uint256 tip{WITH_LOCK(m_cached_tip_mutex, return m_cached_tip_blocks)};145if (!tip.IsNull()) {146return tip;147}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 not151// own `m_cached_tip_mutex` here.152tip = m_node.getBestBlockHash();154LOCK(m_cached_tip_mutex);155// We checked that `m_cached_tip_blocks` is not null above, but then we156// released the mutex `m_cached_tip_mutex`, so it could have changed in the157// meantime. Thus, check again.158if (m_cached_tip_blocks.IsNull()) {159m_cached_tip_blocks = tip;160}161return m_cached_tip_blocks;162}164BlockSource ClientModel::getBlockSource() const165{166if (m_node.isLoadingBlocks()) return BlockSource::DISK;167if (getNumConnections() > 0) return BlockSource::NETWORK;168return BlockSource::NONE;169}171QString ClientModel::getStatusBarWarnings() const172{173return QString::fromStdString(m_node.getWarnings().translated);174}176OptionsModel *ClientModel::getOptionsModel()177{178return optionsModel;179}181PeerTableModel *ClientModel::getPeerTableModel()182{183return peerTableModel;184}186PeerTableSortProxy* ClientModel::peerTableSortProxy()187{188return m_peer_table_sort_proxy;189}191BanTableModel *ClientModel::getBanTableModel()192{193return banTableModel;194}196QString ClientModel::formatFullVersion() const197{198return QString::fromStdString(FormatFullVersion());199}201QString ClientModel::formatSubVersion() const202{203return QString::fromStdString(strSubVersion);204}206bool ClientModel::isReleaseVersion() const207{208return CLIENT_VERSION_IS_RELEASE;209}211QString ClientModel::formatClientStartupTime() const212{213return QDateTime::currentDateTime().addSecs(-TicksSeconds(GetUptime())).toString();214}216QString ClientModel::dataDir() const217{218return GUIUtil::PathToQString(gArgs.GetDataDirNet());219}221QString ClientModel::blocksDir() const222{223return GUIUtil::PathToQString(gArgs.GetBlocksDirPath());224}226void ClientModel::TipChanged(SynchronizationState sync_state, interfaces::BlockTip tip, double verification_progress, SyncType synctype)227{228if (synctype == SyncType::HEADER_SYNC) {229// cache best headers time and height to reduce future cs_main locks230cachedBestHeaderHeight = tip.block_height;231cachedBestHeaderTime = tip.block_time;232} else if (synctype == SyncType::BLOCK_SYNC) {233m_cached_num_blocks = tip.block_height;234WITH_LOCK(m_cached_tip_mutex, m_cached_tip_blocks = tip.block_hash;);235}237// Throttle GUI notifications about (a) blocks during initial sync, and (b) both blocks and headers during reindex.238const bool throttle = (sync_state != SynchronizationState::POST_INIT && synctype == SyncType::BLOCK_SYNC) || sync_state == SynchronizationState::INIT_REINDEX;239const auto now{throttle ? SteadyClock::now() : SteadyClock::time_point{}};240auto& nLastUpdateNotification = synctype != SyncType::BLOCK_SYNC ? g_last_header_tip_update_notification : g_last_block_tip_update_notification;241if (throttle && now < nLastUpdateNotification + MODEL_UPDATE_DELAY) {242return;243}245Q_EMIT numBlocksChanged(tip.block_height, QDateTime::fromSecsSinceEpoch(tip.block_time), verification_progress, synctype, sync_state);246nLastUpdateNotification = now;247}249void ClientModel::subscribeToCoreSignals()250{
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.