mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-24 12:41:41 -05:00

-BEGIN VERIFY SCRIPT- regex_string='^(?!//).*(AC_APPLE_UNIVERSAL_BUILD|BOOST_PROCESS_USE_STD_FS|CHAR_EQUALS_INT8|CLIENT_VERSION_BUILD|CLIENT_VERSION_IS_RELEASE|CLIENT_VERSION_MAJOR|CLIENT_VERSION_MINOR|COPYRIGHT_HOLDERS|COPYRIGHT_HOLDERS_FINAL|COPYRIGHT_HOLDERS_SUBSTITUTION|COPYRIGHT_YEAR|ENABLE_ARM_SHANI|ENABLE_AVX2|ENABLE_EXTERNAL_SIGNER|ENABLE_SSE41|ENABLE_TRACING|ENABLE_WALLET|ENABLE_X86_SHANI|ENABLE_ZMQ|HAVE_BOOST|HAVE_BUILTIN_CLZL|HAVE_BUILTIN_CLZLL|HAVE_BYTESWAP_H|HAVE_CLMUL|HAVE_CONSENSUS_LIB|HAVE_CXX20|HAVE_DECL_BE16TOH|HAVE_DECL_BE32TOH|HAVE_DECL_BE64TOH|HAVE_DECL_BSWAP_16|HAVE_DECL_BSWAP_32|HAVE_DECL_BSWAP_64|HAVE_DECL_FORK|HAVE_DECL_FREEIFADDRS|HAVE_DECL_GETIFADDRS|HAVE_DECL_HTOBE16|HAVE_DECL_HTOBE32|HAVE_DECL_HTOBE64|HAVE_DECL_HTOLE16|HAVE_DECL_HTOLE32|HAVE_DECL_HTOLE64|HAVE_DECL_LE16TOH|HAVE_DECL_LE32TOH|HAVE_DECL_LE64TOH|HAVE_DECL_PIPE2|HAVE_DECL_SETSID|HAVE_DECL_STRERROR_R|HAVE_DEFAULT_VISIBILITY_ATTRIBUTE|HAVE_DLFCN_H|HAVE_DLLEXPORT_ATTRIBUTE|HAVE_ENDIAN_H|HAVE_EVHTTP_CONNECTION_GET_PEER_CONST_CHAR|HAVE_FDATASYNC|HAVE_GETENTROPY_RAND|HAVE_GETRANDOM|HAVE_GMTIME_R|HAVE_INTTYPES_H|HAVE_LIBADVAPI32|HAVE_LIBCOMCTL32|HAVE_LIBCOMDLG32|HAVE_LIBGDI32|HAVE_LIBIPHLPAPI|HAVE_LIBKERNEL32|HAVE_LIBOLE32|HAVE_LIBOLEAUT32|HAVE_LIBSHELL32|HAVE_LIBSHLWAPI|HAVE_LIBUSER32|HAVE_LIBUUID|HAVE_LIBWINMM|HAVE_LIBWS2_32|HAVE_MALLOC_INFO|HAVE_MALLOPT_ARENA_MAX|HAVE_MINIUPNPC_MINIUPNPC_H|HAVE_MINIUPNPC_UPNPCOMMANDS_H|HAVE_MINIUPNPC_UPNPERRORS_H|HAVE_NATPMP_H|HAVE_O_CLOEXEC|HAVE_POSIX_FALLOCATE|HAVE_PTHREAD|HAVE_PTHREAD_PRIO_INHERIT|HAVE_STDINT_H|HAVE_STDIO_H|HAVE_STDLIB_H|HAVE_STRERROR_R|HAVE_STRINGS_H|HAVE_STRING_H|HAVE_STRONG_GETAUXVAL|HAVE_SYSCTL|HAVE_SYSCTL_ARND|HAVE_SYSTEM|HAVE_SYS_ENDIAN_H|HAVE_SYS_PRCTL_H|HAVE_SYS_RESOURCES_H|HAVE_SYS_SELECT_H|HAVE_SYS_STAT_H|HAVE_SYS_SYSCTL_H|HAVE_SYS_TYPES_H|HAVE_SYS_VMMETER_H|HAVE_THREAD_LOCAL|HAVE_TIMINGSAFE_BCMP|HAVE_UNISTD_H|HAVE_VM_VM_PARAM_H|LT_OBJDIR|PACKAGE_BUGREPORT|PACKAGE_NAME|PACKAGE_STRING|PACKAGE_TARNAME|PACKAGE_URL|PACKAGE_VERSION|PTHREAD_CREATE_JOINABLE|QT_QPA_PLATFORM_ANDROID|QT_QPA_PLATFORM_COCOA|QT_QPA_PLATFORM_MINIMAL|QT_QPA_PLATFORM_WINDOWS|QT_QPA_PLATFORM_XCB|QT_STATICPLUGIN|STDC_HEADERS|STRERROR_R_CHAR_P|USE_ASM|USE_BDB|USE_DBUS|USE_NATPMP|USE_QRCODE|USE_SQLITE|USE_UPNP|_FILE_OFFSET_BITS|_LARGE_FILES)' exclusion_files=":(exclude)src/minisketch :(exclude)src/crc32c :(exclude)src/secp256k1 :(exclude)src/crypto/sha256_arm_shani.cpp :(exclude)src/crypto/sha256_avx2.cpp :(exclude)src/crypto/sha256_sse41.cpp :(exclude)src/crypto/sha256_x86_shani.cpp" git grep --perl-regexp --files-with-matches "$regex_string" -- '*.cpp' $exclusion_files | xargs git grep -L "bitcoin-config.h" | while read -r file; do line_number=$(awk -v my_file="$file" '/\/\/ file COPYING or https?:\/\/www.opensource.org\/licenses\/mit-license.php\./ {line = NR} /^\/\// && NR == line + 1 {while(getline && /^\/\//) line = NR} END {print line+1}' "$file"); sed -i "${line_number}i\\\\n\#if defined(HAVE_CONFIG_H)\\n#include <config/bitcoin-config.h>\\n\#endif" "$file"; done; git grep --perl-regexp --files-with-matches "$regex_string" -- '*.h' $exclusion_files | xargs git grep -L "bitcoin-config.h" | while read -r file; do sed -i "/#define.*_H/a \\\\n\#if defined(HAVE_CONFIG_H)\\n#include <config/bitcoin-config.h>\\n\#endif" "$file"; done; for file in $(git grep --files-with-matches 'bitcoin-config.h' -- '*.cpp' '*.h' $exclusion_files); do if ! grep -q --perl-regexp "$regex_string" $file; then sed -i '/HAVE_CONFIG_H/{N;N;N;d;}' $file; fi; done; -END VERIFY SCRIPT- The first command creates a regular expression for matching all bitcoin-config.h symbols in the following form: ^(?!//).*(AC_APPLE_UNIVERSAL_BUILD|BOOST_PROCESS_USE_STD_FS|...|_LARGE_FILES). It was generated with: ./autogen.sh && printf '^(?!//).*(%s)' $(awk '/^#undef/ {print $2}' src/config/bitcoin-config.h.in | paste -sd "|" -) The second command holds a list of files and directories that should not be processed. These include subtree directories as well as some crypto files that already get their symbols through the makefile. The third command checks for missing bitcoin-config headers in .cpp files and adds the header if it is missing. The fourth command checks for missing bitcoin-config headers in .h files and adds the header if it is missing. The fifth command checks for unneeded bitcoin-config headers in sources files and removes the header if it is unneeded.
292 lines
9.3 KiB
C++
292 lines
9.3 KiB
C++
// Copyright (c) 2011-2022 The Bitcoin Core developers
|
|
// Distributed under the MIT software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#if defined(HAVE_CONFIG_H)
|
|
#include <config/bitcoin-config.h>
|
|
#endif
|
|
|
|
#include <qt/clientmodel.h>
|
|
|
|
#include <qt/bantablemodel.h>
|
|
#include <qt/guiconstants.h>
|
|
#include <qt/guiutil.h>
|
|
#include <qt/peertablemodel.h>
|
|
#include <qt/peertablesortproxy.h>
|
|
|
|
#include <clientversion.h>
|
|
#include <common/args.h>
|
|
#include <common/system.h>
|
|
#include <interfaces/handler.h>
|
|
#include <interfaces/node.h>
|
|
#include <net.h>
|
|
#include <netbase.h>
|
|
#include <util/threadnames.h>
|
|
#include <util/time.h>
|
|
#include <validation.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <QDebug>
|
|
#include <QMetaObject>
|
|
#include <QThread>
|
|
#include <QTimer>
|
|
|
|
static SteadyClock::time_point g_last_header_tip_update_notification{};
|
|
static SteadyClock::time_point g_last_block_tip_update_notification{};
|
|
|
|
ClientModel::ClientModel(interfaces::Node& node, OptionsModel *_optionsModel, QObject *parent) :
|
|
QObject(parent),
|
|
m_node(node),
|
|
optionsModel(_optionsModel),
|
|
m_thread(new QThread(this))
|
|
{
|
|
cachedBestHeaderHeight = -1;
|
|
cachedBestHeaderTime = -1;
|
|
|
|
peerTableModel = new PeerTableModel(m_node, this);
|
|
m_peer_table_sort_proxy = new PeerTableSortProxy(this);
|
|
m_peer_table_sort_proxy->setSourceModel(peerTableModel);
|
|
|
|
banTableModel = new BanTableModel(m_node, this);
|
|
|
|
QTimer* timer = new QTimer;
|
|
timer->setInterval(MODEL_UPDATE_DELAY);
|
|
connect(timer, &QTimer::timeout, [this] {
|
|
// no locking required at this point
|
|
// the following calls will acquire the required lock
|
|
Q_EMIT mempoolSizeChanged(m_node.getMempoolSize(), m_node.getMempoolDynamicUsage());
|
|
Q_EMIT bytesChanged(m_node.getTotalBytesRecv(), m_node.getTotalBytesSent());
|
|
});
|
|
connect(m_thread, &QThread::finished, timer, &QObject::deleteLater);
|
|
connect(m_thread, &QThread::started, [timer] { timer->start(); });
|
|
// move timer to thread so that polling doesn't disturb main event loop
|
|
timer->moveToThread(m_thread);
|
|
m_thread->start();
|
|
QTimer::singleShot(0, timer, []() {
|
|
util::ThreadRename("qt-clientmodl");
|
|
});
|
|
|
|
subscribeToCoreSignals();
|
|
}
|
|
|
|
ClientModel::~ClientModel()
|
|
{
|
|
unsubscribeFromCoreSignals();
|
|
|
|
m_thread->quit();
|
|
m_thread->wait();
|
|
}
|
|
|
|
int ClientModel::getNumConnections(unsigned int flags) const
|
|
{
|
|
ConnectionDirection connections = ConnectionDirection::None;
|
|
|
|
if(flags == CONNECTIONS_IN)
|
|
connections = ConnectionDirection::In;
|
|
else if (flags == CONNECTIONS_OUT)
|
|
connections = ConnectionDirection::Out;
|
|
else if (flags == CONNECTIONS_ALL)
|
|
connections = ConnectionDirection::Both;
|
|
|
|
return m_node.getNodeCount(connections);
|
|
}
|
|
|
|
int ClientModel::getHeaderTipHeight() const
|
|
{
|
|
if (cachedBestHeaderHeight == -1) {
|
|
// make sure we initially populate the cache via a cs_main lock
|
|
// otherwise we need to wait for a tip update
|
|
int height;
|
|
int64_t blockTime;
|
|
if (m_node.getHeaderTip(height, blockTime)) {
|
|
cachedBestHeaderHeight = height;
|
|
cachedBestHeaderTime = blockTime;
|
|
}
|
|
}
|
|
return cachedBestHeaderHeight;
|
|
}
|
|
|
|
int64_t ClientModel::getHeaderTipTime() const
|
|
{
|
|
if (cachedBestHeaderTime == -1) {
|
|
int height;
|
|
int64_t blockTime;
|
|
if (m_node.getHeaderTip(height, blockTime)) {
|
|
cachedBestHeaderHeight = height;
|
|
cachedBestHeaderTime = blockTime;
|
|
}
|
|
}
|
|
return cachedBestHeaderTime;
|
|
}
|
|
|
|
int ClientModel::getNumBlocks() const
|
|
{
|
|
if (m_cached_num_blocks == -1) {
|
|
m_cached_num_blocks = m_node.getNumBlocks();
|
|
}
|
|
return m_cached_num_blocks;
|
|
}
|
|
|
|
uint256 ClientModel::getBestBlockHash()
|
|
{
|
|
uint256 tip{WITH_LOCK(m_cached_tip_mutex, return m_cached_tip_blocks)};
|
|
|
|
if (!tip.IsNull()) {
|
|
return tip;
|
|
}
|
|
|
|
// Lock order must be: first `cs_main`, then `m_cached_tip_mutex`.
|
|
// The following will lock `cs_main` (and release it), so we must not
|
|
// own `m_cached_tip_mutex` here.
|
|
tip = m_node.getBestBlockHash();
|
|
|
|
LOCK(m_cached_tip_mutex);
|
|
// We checked that `m_cached_tip_blocks` is not null above, but then we
|
|
// released the mutex `m_cached_tip_mutex`, so it could have changed in the
|
|
// meantime. Thus, check again.
|
|
if (m_cached_tip_blocks.IsNull()) {
|
|
m_cached_tip_blocks = tip;
|
|
}
|
|
return m_cached_tip_blocks;
|
|
}
|
|
|
|
BlockSource ClientModel::getBlockSource() const
|
|
{
|
|
if (m_node.isLoadingBlocks()) return BlockSource::DISK;
|
|
if (getNumConnections() > 0) return BlockSource::NETWORK;
|
|
return BlockSource::NONE;
|
|
}
|
|
|
|
QString ClientModel::getStatusBarWarnings() const
|
|
{
|
|
return QString::fromStdString(m_node.getWarnings().translated);
|
|
}
|
|
|
|
OptionsModel *ClientModel::getOptionsModel()
|
|
{
|
|
return optionsModel;
|
|
}
|
|
|
|
PeerTableModel *ClientModel::getPeerTableModel()
|
|
{
|
|
return peerTableModel;
|
|
}
|
|
|
|
PeerTableSortProxy* ClientModel::peerTableSortProxy()
|
|
{
|
|
return m_peer_table_sort_proxy;
|
|
}
|
|
|
|
BanTableModel *ClientModel::getBanTableModel()
|
|
{
|
|
return banTableModel;
|
|
}
|
|
|
|
QString ClientModel::formatFullVersion() const
|
|
{
|
|
return QString::fromStdString(FormatFullVersion());
|
|
}
|
|
|
|
QString ClientModel::formatSubVersion() const
|
|
{
|
|
return QString::fromStdString(strSubVersion);
|
|
}
|
|
|
|
bool ClientModel::isReleaseVersion() const
|
|
{
|
|
return CLIENT_VERSION_IS_RELEASE;
|
|
}
|
|
|
|
QString ClientModel::formatClientStartupTime() const
|
|
{
|
|
return QDateTime::fromSecsSinceEpoch(GetStartupTime()).toString();
|
|
}
|
|
|
|
QString ClientModel::dataDir() const
|
|
{
|
|
return GUIUtil::PathToQString(gArgs.GetDataDirNet());
|
|
}
|
|
|
|
QString ClientModel::blocksDir() const
|
|
{
|
|
return GUIUtil::PathToQString(gArgs.GetBlocksDirPath());
|
|
}
|
|
|
|
void ClientModel::TipChanged(SynchronizationState sync_state, interfaces::BlockTip tip, double verification_progress, SyncType synctype)
|
|
{
|
|
if (synctype == SyncType::HEADER_SYNC) {
|
|
// cache best headers time and height to reduce future cs_main locks
|
|
cachedBestHeaderHeight = tip.block_height;
|
|
cachedBestHeaderTime = tip.block_time;
|
|
} else if (synctype == SyncType::BLOCK_SYNC) {
|
|
m_cached_num_blocks = tip.block_height;
|
|
WITH_LOCK(m_cached_tip_mutex, m_cached_tip_blocks = tip.block_hash;);
|
|
}
|
|
|
|
// Throttle GUI notifications about (a) blocks during initial sync, and (b) both blocks and headers during reindex.
|
|
const bool throttle = (sync_state != SynchronizationState::POST_INIT && synctype == SyncType::BLOCK_SYNC) || sync_state == SynchronizationState::INIT_REINDEX;
|
|
const auto now{throttle ? SteadyClock::now() : SteadyClock::time_point{}};
|
|
auto& nLastUpdateNotification = synctype != SyncType::BLOCK_SYNC ? g_last_header_tip_update_notification : g_last_block_tip_update_notification;
|
|
if (throttle && now < nLastUpdateNotification + MODEL_UPDATE_DELAY) {
|
|
return;
|
|
}
|
|
|
|
Q_EMIT numBlocksChanged(tip.block_height, QDateTime::fromSecsSinceEpoch(tip.block_time), verification_progress, synctype, sync_state);
|
|
nLastUpdateNotification = now;
|
|
}
|
|
|
|
void ClientModel::subscribeToCoreSignals()
|
|
{
|
|
m_handler_show_progress = m_node.handleShowProgress(
|
|
[this](const std::string& title, int progress, [[maybe_unused]] bool resume_possible) {
|
|
Q_EMIT showProgress(QString::fromStdString(title), progress);
|
|
});
|
|
m_handler_notify_num_connections_changed = m_node.handleNotifyNumConnectionsChanged(
|
|
[this](int new_num_connections) {
|
|
Q_EMIT numConnectionsChanged(new_num_connections);
|
|
});
|
|
m_handler_notify_network_active_changed = m_node.handleNotifyNetworkActiveChanged(
|
|
[this](bool network_active) {
|
|
Q_EMIT networkActiveChanged(network_active);
|
|
});
|
|
m_handler_notify_alert_changed = m_node.handleNotifyAlertChanged(
|
|
[this]() {
|
|
qDebug() << "ClientModel: NotifyAlertChanged";
|
|
Q_EMIT alertsChanged(getStatusBarWarnings());
|
|
});
|
|
m_handler_banned_list_changed = m_node.handleBannedListChanged(
|
|
[this]() {
|
|
qDebug() << "ClienModel: Requesting update for peer banlist";
|
|
QMetaObject::invokeMethod(banTableModel, [this] { banTableModel->refresh(); });
|
|
});
|
|
m_handler_notify_block_tip = m_node.handleNotifyBlockTip(
|
|
[this](SynchronizationState sync_state, interfaces::BlockTip tip, double verification_progress) {
|
|
TipChanged(sync_state, tip, verification_progress, SyncType::BLOCK_SYNC);
|
|
});
|
|
m_handler_notify_header_tip = m_node.handleNotifyHeaderTip(
|
|
[this](SynchronizationState sync_state, interfaces::BlockTip tip, bool presync) {
|
|
TipChanged(sync_state, tip, /*verification_progress=*/0.0, presync ? SyncType::HEADER_PRESYNC : SyncType::HEADER_SYNC);
|
|
});
|
|
}
|
|
|
|
void ClientModel::unsubscribeFromCoreSignals()
|
|
{
|
|
m_handler_show_progress->disconnect();
|
|
m_handler_notify_num_connections_changed->disconnect();
|
|
m_handler_notify_network_active_changed->disconnect();
|
|
m_handler_notify_alert_changed->disconnect();
|
|
m_handler_banned_list_changed->disconnect();
|
|
m_handler_notify_block_tip->disconnect();
|
|
m_handler_notify_header_tip->disconnect();
|
|
}
|
|
|
|
bool ClientModel::getProxyInfo(std::string& ip_port) const
|
|
{
|
|
Proxy ipv4, ipv6;
|
|
if (m_node.getProxy((Network) 1, ipv4) && m_node.getProxy((Network) 2, ipv6)) {
|
|
ip_port = ipv4.proxy.ToStringAddrPort();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|