merge bitcoin#17993: Balance/TxStatus polling update based on last block hash

This commit is contained in:
Kittywhiskers Van Gogh 2023-04-10 19:16:08 +00:00 committed by PastaPastaPasta
parent 091eda40c9
commit aef3dd7350
12 changed files with 68 additions and 42 deletions

View File

@ -297,6 +297,11 @@ public:
LOCK(::cs_main); LOCK(::cs_main);
return ::ChainActive().Height(); return ::ChainActive().Height();
} }
uint256 getBestBlockHash() override
{
const CBlockIndex* tip = WITH_LOCK(::cs_main, return ::ChainActive().Tip());
return tip ? tip->GetBlockHash() : Params().GenesisBlock().GetHash();
}
int64_t getLastBlockTime() override int64_t getLastBlockTime() override
{ {
LOCK(::cs_main); LOCK(::cs_main);
@ -395,7 +400,7 @@ public:
std::unique_ptr<Handler> handleNotifyBlockTip(NotifyBlockTipFn fn) override std::unique_ptr<Handler> handleNotifyBlockTip(NotifyBlockTipFn fn) override
{ {
return MakeHandler(::uiInterface.NotifyBlockTip_connect([fn](bool initial_download, const CBlockIndex* block) { return MakeHandler(::uiInterface.NotifyBlockTip_connect([fn](bool initial_download, const CBlockIndex* block) {
fn(initial_download, block->nHeight, block->GetBlockTime(), block->GetBlockHash().ToString(), fn(initial_download, BlockTip{block->nHeight, block->GetBlockTime(), block->GetBlockHash()},
GuessVerificationProgress(Params().TxData(), block)); GuessVerificationProgress(Params().TxData(), block));
})); }));
} }
@ -409,7 +414,7 @@ public:
{ {
return MakeHandler( return MakeHandler(
::uiInterface.NotifyHeaderTip_connect([fn](bool initial_download, const CBlockIndex* block) { ::uiInterface.NotifyHeaderTip_connect([fn](bool initial_download, const CBlockIndex* block) {
fn(initial_download, block->nHeight, block->GetBlockTime(), block->GetBlockHash().ToString(), fn(initial_download, BlockTip{block->nHeight, block->GetBlockTime(), block->GetBlockHash()},
/* verification progress is unused when a header was received */ 0); /* verification progress is unused when a header was received */ 0);
})); }));
} }

View File

@ -38,6 +38,7 @@ struct NodeContext;
namespace interfaces { namespace interfaces {
class Handler; class Handler;
class WalletClient; class WalletClient;
struct BlockTip;
//! Interface for the src/evo part of a dash node (dashd process). //! Interface for the src/evo part of a dash node (dashd process).
class EVO class EVO
@ -229,6 +230,9 @@ public:
//! Get num blocks. //! Get num blocks.
virtual int getNumBlocks() = 0; virtual int getNumBlocks() = 0;
//! Get best block hash.
virtual uint256 getBestBlockHash() = 0;
//! Get last block time. //! Get last block time.
virtual int64_t getLastBlockTime() = 0; virtual int64_t getLastBlockTime() = 0;
@ -327,7 +331,7 @@ public:
//! Register handler for block tip messages. //! Register handler for block tip messages.
using NotifyBlockTipFn = using NotifyBlockTipFn =
std::function<void(bool initial_download, int height, int64_t block_time, const std::string& block_hash, double verification_progress)>; std::function<void(bool initial_download, interfaces::BlockTip tip, double verification_progress)>;
virtual std::unique_ptr<Handler> handleNotifyBlockTip(NotifyBlockTipFn fn) = 0; virtual std::unique_ptr<Handler> handleNotifyBlockTip(NotifyBlockTipFn fn) = 0;
//! Register handler for chainlock messages. //! Register handler for chainlock messages.
@ -337,7 +341,7 @@ public:
//! Register handler for header tip messages. //! Register handler for header tip messages.
using NotifyHeaderTipFn = using NotifyHeaderTipFn =
std::function<void(bool initial_download, int height, int64_t block_time, const std::string& block_hash, double verification_progress)>; std::function<void(bool initial_download, interfaces::BlockTip tip, double verification_progress)>;
virtual std::unique_ptr<Handler> handleNotifyHeaderTip(NotifyHeaderTipFn fn) = 0; virtual std::unique_ptr<Handler> handleNotifyHeaderTip(NotifyHeaderTipFn fn) = 0;
//! Register handler for masternode list update messages. //! Register handler for masternode list update messages.
@ -359,6 +363,12 @@ public:
//! Return implementation of Node interface. //! Return implementation of Node interface.
std::unique_ptr<Node> MakeNode(NodeContext* context = nullptr); std::unique_ptr<Node> MakeNode(NodeContext* context = nullptr);
//! Block tip (could be a header or not, depends on the subscribed signal).
struct BlockTip {
int block_height;
int64_t block_time;
uint256 block_hash;
};
} // namespace interfaces } // namespace interfaces
#endif // BITCOIN_INTERFACES_NODE_H #endif // BITCOIN_INTERFACES_NODE_H

View File

@ -417,13 +417,13 @@ public:
} }
return result; return result;
} }
bool tryGetBalances(WalletBalances& balances, int& num_blocks) override bool tryGetBalances(WalletBalances& balances, uint256& block_hash) override
{ {
TRY_LOCK(m_wallet->cs_wallet, locked_wallet); TRY_LOCK(m_wallet->cs_wallet, locked_wallet);
if (!locked_wallet) { if (!locked_wallet) {
return false; return false;
} }
num_blocks = m_wallet->GetLastBlockHeight(); block_hash = m_wallet->GetLastBlockHash();
balances = getBalances(); balances = getBalances();
return true; return true;
} }

View File

@ -229,7 +229,7 @@ public:
virtual WalletBalances getBalances() = 0; virtual WalletBalances getBalances() = 0;
//! Get balances if possible without blocking. //! Get balances if possible without blocking.
virtual bool tryGetBalances(WalletBalances& balances, int& num_blocks) = 0; virtual bool tryGetBalances(WalletBalances& balances, uint256& block_hash) = 0;
//! Get balance. //! Get balance.
virtual CAmount getBalance() = 0; virtual CAmount getBalance() = 0;

View File

@ -145,6 +145,15 @@ int ClientModel::getNumBlocks() const
return m_cached_num_blocks; return m_cached_num_blocks;
} }
uint256 ClientModel::getBestBlockHash()
{
LOCK(m_cached_tip_mutex);
if (m_cached_tip_blocks.IsNull()) {
m_cached_tip_blocks = m_node.getBestBlockHash();
}
return m_cached_tip_blocks;
}
void ClientModel::updateNumConnections(int numConnections) void ClientModel::updateNumConnections(int numConnections)
{ {
Q_EMIT numConnectionsChanged(numConnections); Q_EMIT numConnectionsChanged(numConnections);
@ -266,7 +275,7 @@ static void BannedListChanged(ClientModel *clientmodel)
assert(invoked); assert(invoked);
} }
static void BlockTipChanged(ClientModel *clientmodel, bool initialSync, int height, int64_t blockTime, const std::string& strBlockHash, double verificationProgress, bool fHeader) static void BlockTipChanged(ClientModel *clientmodel, bool initialSync, interfaces::BlockTip tip, double verificationProgress, bool fHeader)
{ {
// lock free async UI updates in case we have a new block tip // lock free async UI updates in case we have a new block tip
// during initial sync, only update the UI if the last update // during initial sync, only update the UI if the last update
@ -279,19 +288,20 @@ static void BlockTipChanged(ClientModel *clientmodel, bool initialSync, int heig
if (fHeader) { if (fHeader) {
// cache best headers time and height to reduce future cs_main locks // cache best headers time and height to reduce future cs_main locks
clientmodel->cachedBestHeaderHeight = height; clientmodel->cachedBestHeaderHeight = tip.block_height;
clientmodel->cachedBestHeaderTime = blockTime; clientmodel->cachedBestHeaderTime = tip.block_time;
} else { } else {
clientmodel->m_cached_num_blocks = height; clientmodel->m_cached_num_blocks = tip.block_height;
WITH_LOCK(clientmodel->m_cached_tip_mutex, clientmodel->m_cached_tip_blocks = tip.block_hash;);
} }
// During initial sync, block notifications, and header notifications from reindexing are both throttled. // During initial sync, block notifications, and header notifications from reindexing are both throttled.
if (!initialSync || (fHeader && !clientmodel->node().getReindex()) || now - nLastUpdateNotification > MODEL_UPDATE_DELAY) { if (!initialSync || (fHeader && !clientmodel->node().getReindex()) || now - nLastUpdateNotification > MODEL_UPDATE_DELAY) {
//pass an async signal to the UI thread //pass an async signal to the UI thread
bool invoked = QMetaObject::invokeMethod(clientmodel, "numBlocksChanged", Qt::QueuedConnection, bool invoked = QMetaObject::invokeMethod(clientmodel, "numBlocksChanged", Qt::QueuedConnection,
Q_ARG(int, height), Q_ARG(int, tip.block_height),
Q_ARG(QDateTime, QDateTime::fromTime_t(blockTime)), Q_ARG(QDateTime, QDateTime::fromTime_t(tip.block_time)),
Q_ARG(QString, QString::fromStdString(strBlockHash)), Q_ARG(QString, QString::fromStdString(tip.block_hash.ToString())),
Q_ARG(double, verificationProgress), Q_ARG(double, verificationProgress),
Q_ARG(bool, fHeader)); Q_ARG(bool, fHeader));
assert(invoked); assert(invoked);
@ -328,9 +338,9 @@ void ClientModel::subscribeToCoreSignals()
m_handler_notify_network_active_changed = m_node.handleNotifyNetworkActiveChanged(std::bind(NotifyNetworkActiveChanged, this, std::placeholders::_1)); m_handler_notify_network_active_changed = m_node.handleNotifyNetworkActiveChanged(std::bind(NotifyNetworkActiveChanged, this, std::placeholders::_1));
m_handler_notify_alert_changed = m_node.handleNotifyAlertChanged(std::bind(NotifyAlertChanged, this)); m_handler_notify_alert_changed = m_node.handleNotifyAlertChanged(std::bind(NotifyAlertChanged, this));
m_handler_banned_list_changed = m_node.handleBannedListChanged(std::bind(BannedListChanged, this)); m_handler_banned_list_changed = m_node.handleBannedListChanged(std::bind(BannedListChanged, this));
m_handler_notify_block_tip = m_node.handleNotifyBlockTip(std::bind(BlockTipChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, false)); m_handler_notify_block_tip = m_node.handleNotifyBlockTip(std::bind(BlockTipChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, false));
m_handler_notify_chainlock = m_node.handleNotifyChainLock(std::bind(NotifyChainLock, this, std::placeholders::_1, std::placeholders::_2)); m_handler_notify_chainlock = m_node.handleNotifyChainLock(std::bind(NotifyChainLock, this, std::placeholders::_1, std::placeholders::_2));
m_handler_notify_header_tip = m_node.handleNotifyHeaderTip(std::bind(BlockTipChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, true)); m_handler_notify_header_tip = m_node.handleNotifyHeaderTip(std::bind(BlockTipChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, true));
m_handler_notify_masternodelist_changed = m_node.handleNotifyMasternodeListChanged(std::bind(NotifyMasternodeListChanged, this, std::placeholders::_1)); m_handler_notify_masternodelist_changed = m_node.handleNotifyMasternodeListChanged(std::bind(NotifyMasternodeListChanged, this, std::placeholders::_1));
m_handler_notify_additional_data_sync_progess_changed = m_node.handleNotifyAdditionalDataSyncProgressChanged(std::bind(NotifyAdditionalDataSyncProgressChanged, this, std::placeholders::_1)); m_handler_notify_additional_data_sync_progess_changed = m_node.handleNotifyAdditionalDataSyncProgressChanged(std::bind(NotifyAdditionalDataSyncProgressChanged, this, std::placeholders::_1));
} }

View File

@ -14,6 +14,7 @@
#include <atomic> #include <atomic>
#include <memory> #include <memory>
#include <uint256.h>
class BanTableModel; class BanTableModel;
class OptionsModel; class OptionsModel;
@ -62,6 +63,7 @@ public:
//! Return number of connections, default is in- and outbound (total) //! Return number of connections, default is in- and outbound (total)
int getNumConnections(unsigned int flags = CONNECTIONS_ALL) const; int getNumConnections(unsigned int flags = CONNECTIONS_ALL) const;
int getNumBlocks() const; int getNumBlocks() const;
uint256 getBestBlockHash();
int getHeaderTipHeight() const; int getHeaderTipHeight() const;
int64_t getHeaderTipTime() const; int64_t getHeaderTipTime() const;
@ -85,11 +87,14 @@ public:
bool getProxyInfo(std::string& ip_port) const; bool getProxyInfo(std::string& ip_port) const;
// caches for the best header, number of blocks // caches for the best header: hash, number of blocks and block time
mutable std::atomic<int> cachedBestHeaderHeight; mutable std::atomic<int> cachedBestHeaderHeight;
mutable std::atomic<int64_t> cachedBestHeaderTime; mutable std::atomic<int64_t> cachedBestHeaderTime;
mutable std::atomic<int> m_cached_num_blocks{-1}; mutable std::atomic<int> m_cached_num_blocks{-1};
Mutex m_cached_tip_mutex;
uint256 m_cached_tip_blocks GUARDED_BY(m_cached_tip_mutex){};
private: private:
interfaces::Node& m_node; interfaces::Node& m_node;
std::unique_ptr<interfaces::Handler> m_handler_show_progress; std::unique_ptr<interfaces::Handler> m_handler_show_progress;

View File

@ -257,7 +257,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(interfaces::Wal
return parts; return parts;
} }
void TransactionRecord::updateStatus(const interfaces::WalletTxStatus& wtx, int numBlocks, int chainLockHeight, int64_t block_time) void TransactionRecord::updateStatus(const interfaces::WalletTxStatus& wtx, const uint256& block_hash, int numBlocks, int chainLockHeight, int64_t block_time)
{ {
// Determine transaction status // Determine transaction status
@ -269,7 +269,7 @@ void TransactionRecord::updateStatus(const interfaces::WalletTxStatus& wtx, int
idx); idx);
status.countsForBalance = wtx.is_trusted && !(wtx.blocks_to_maturity > 0); status.countsForBalance = wtx.is_trusted && !(wtx.blocks_to_maturity > 0);
status.depth = wtx.depth_in_main_chain; status.depth = wtx.depth_in_main_chain;
status.cur_num_blocks = numBlocks; status.m_cur_block_hash = block_hash;
status.cachedChainLockHeight = chainLockHeight; status.cachedChainLockHeight = chainLockHeight;
status.lockedByChainLocks = wtx.is_chainlocked; status.lockedByChainLocks = wtx.is_chainlocked;
status.lockedByInstantSend = wtx.is_islocked; status.lockedByInstantSend = wtx.is_islocked;
@ -331,9 +331,9 @@ void TransactionRecord::updateStatus(const interfaces::WalletTxStatus& wtx, int
status.needsUpdate = false; status.needsUpdate = false;
} }
bool TransactionRecord::statusUpdateNeeded(int numBlocks, int chainLockHeight) const bool TransactionRecord::statusUpdateNeeded(const uint256& block_hash, int chainLockHeight) const
{ {
return status.cur_num_blocks != numBlocks || status.needsUpdate return status.m_cur_block_hash != block_hash || status.needsUpdate
|| (!status.lockedByChainLocks && status.cachedChainLockHeight != chainLockHeight); || (!status.lockedByChainLocks && status.cachedChainLockHeight != chainLockHeight);
} }

View File

@ -26,7 +26,7 @@ class TransactionStatus
public: public:
TransactionStatus(): TransactionStatus():
countsForBalance(false), lockedByInstantSend(false), lockedByChainLocks(false), sortKey(""), countsForBalance(false), lockedByInstantSend(false), lockedByChainLocks(false), sortKey(""),
matures_in(0), status(Unconfirmed), depth(0), open_for(0), cur_num_blocks(-1), matures_in(0), status(Unconfirmed), depth(0), open_for(0),
cachedChainLockHeight(-1), needsUpdate(false) cachedChainLockHeight(-1), needsUpdate(false)
{ } { }
@ -67,8 +67,8 @@ public:
finalization */ finalization */
/**@}*/ /**@}*/
/** Current number of blocks (to know whether cached status is still valid) */ /** Current block hash (to know whether cached status is still valid) */
int cur_num_blocks; uint256 m_cur_block_hash{};
//** Know when to update transaction for chainlocks **/ //** Know when to update transaction for chainlocks **/
int cachedChainLockHeight; int cachedChainLockHeight;
@ -161,11 +161,11 @@ public:
/** Update status from core wallet tx. /** Update status from core wallet tx.
*/ */
void updateStatus(const interfaces::WalletTxStatus& wtx, int numBlocks, int chainLockHeight, int64_t block_time); void updateStatus(const interfaces::WalletTxStatus& wtx, const uint256& block_hash, int numBlocks, int chainLockHeight, int64_t block_time);
/** Return whether a status update is needed. /** Return whether a status update is needed.
*/ */
bool statusUpdateNeeded(int numBlocks, int chainLockHeight) const; bool statusUpdateNeeded(const uint256& block_hash, int chainLockHeight) const;
/** Update label from address book. /** Update label from address book.
*/ */

View File

@ -188,7 +188,7 @@ public:
return cachedWallet.size(); return cachedWallet.size();
} }
TransactionRecord *index(interfaces::Wallet& wallet, const int cur_num_blocks, const int idx) TransactionRecord* index(interfaces::Wallet& wallet, const uint256& cur_block_hash, const int cur_num_blocks, const int idx)
{ {
if (idx >= 0 && idx < cachedWallet.size()) { if (idx >= 0 && idx < cachedWallet.size()) {
TransactionRecord *rec = &cachedWallet[idx]; TransactionRecord *rec = &cachedWallet[idx];
@ -198,8 +198,8 @@ public:
// Otherwise, simply re-use the cached status. // Otherwise, simply re-use the cached status.
interfaces::WalletTxStatus wtx; interfaces::WalletTxStatus wtx;
int64_t block_time; int64_t block_time;
if (rec->statusUpdateNeeded(cur_num_blocks, parent->getChainLockHeight()) && wallet.tryGetTxStatus(rec->hash, wtx, block_time)) { if (rec->statusUpdateNeeded(cur_block_hash, parent->getChainLockHeight()) && wallet.tryGetTxStatus(rec->hash, wtx, block_time)) {
rec->updateStatus(wtx, cur_num_blocks, parent->getChainLockHeight(), block_time); rec->updateStatus(wtx, cur_block_hash, cur_num_blocks, parent->getChainLockHeight(), block_time);
} }
return rec; return rec;
} }
@ -726,7 +726,7 @@ QVariant TransactionTableModel::headerData(int section, Qt::Orientation orientat
QModelIndex TransactionTableModel::index(int row, int column, const QModelIndex &parent) const QModelIndex TransactionTableModel::index(int row, int column, const QModelIndex &parent) const
{ {
Q_UNUSED(parent); Q_UNUSED(parent);
TransactionRecord *data = priv->index(walletModel->wallet(), walletModel->clientModel().getNumBlocks(), row); TransactionRecord *data = priv->index(walletModel->wallet(), walletModel->clientModel().getBestBlockHash(), walletModel->clientModel().getNumBlocks(), row);
if (data) { if (data) {
return createIndex(row, column, data); return createIndex(row, column, data);
} }

View File

@ -46,7 +46,6 @@ WalletModel::WalletModel(std::unique_ptr<interfaces::Wallet> wallet, ClientModel
transactionTableModel(nullptr), transactionTableModel(nullptr),
recentRequestsTableModel(nullptr), recentRequestsTableModel(nullptr),
cachedEncryptionStatus(Unencrypted), cachedEncryptionStatus(Unencrypted),
cachedNumBlocks(-1),
cachedNumISLocks(0), cachedNumISLocks(0),
cachedCoinJoinRounds(0) cachedCoinJoinRounds(0)
{ {
@ -91,17 +90,17 @@ void WalletModel::pollBalanceChanged()
// holding the locks for a longer time - for example, during a wallet // holding the locks for a longer time - for example, during a wallet
// rescan. // rescan.
interfaces::WalletBalances new_balances; interfaces::WalletBalances new_balances;
int numBlocks = -1; uint256 block_hash;
if (!m_wallet->tryGetBalances(new_balances, numBlocks)) { if (!m_wallet->tryGetBalances(new_balances, block_hash)) {
return; return;
} }
if(fForceCheckBalanceChanged || numBlocks != cachedNumBlocks || node().coinJoinOptions().getRounds() != cachedCoinJoinRounds) if (fForceCheckBalanceChanged || block_hash != m_cached_last_update_tip || node().coinJoinOptions().getRounds() != cachedCoinJoinRounds)
{ {
fForceCheckBalanceChanged = false; fForceCheckBalanceChanged = false;
// Balance and number of transactions might have changed // Balance and number of transactions might have changed
cachedNumBlocks = numBlocks; m_cached_last_update_tip = block_hash;
cachedCoinJoinRounds = node().coinJoinOptions().getRounds(); cachedCoinJoinRounds = node().coinJoinOptions().getRounds();
checkBalanceChanged(new_balances); checkBalanceChanged(new_balances);
@ -137,11 +136,6 @@ void WalletModel::updateChainLockHeight(int chainLockHeight)
fForceCheckBalanceChanged = true; fForceCheckBalanceChanged = true;
} }
int WalletModel::getNumBlocks() const
{
return cachedNumBlocks;
}
int WalletModel::getNumISLocks() const int WalletModel::getNumISLocks() const
{ {
return cachedNumISLocks; return cachedNumISLocks;

View File

@ -191,7 +191,6 @@ public:
bool privateKeysDisabled() const; bool privateKeysDisabled() const;
bool canGetAddresses() const; bool canGetAddresses() const;
int getNumBlocks() const;
int getNumISLocks() const; int getNumISLocks() const;
int getRealOutpointCoinJoinRounds(const COutPoint& outpoint) const; int getRealOutpointCoinJoinRounds(const COutPoint& outpoint) const;
@ -236,10 +235,12 @@ private:
// Cache some values to be able to detect changes // Cache some values to be able to detect changes
interfaces::WalletBalances m_cached_balances; interfaces::WalletBalances m_cached_balances;
EncryptionStatus cachedEncryptionStatus; EncryptionStatus cachedEncryptionStatus;
int cachedNumBlocks;
int cachedNumISLocks; int cachedNumISLocks;
int cachedCoinJoinRounds; int cachedCoinJoinRounds;
// Block hash denoting when the last balance update was done.
uint256 m_cached_last_update_tip{};
void subscribeToCoreSignals(); void subscribeToCoreSignals();
void unsubscribeFromCoreSignals(); void unsubscribeFromCoreSignals();
void checkBalanceChanged(const interfaces::WalletBalances& new_balances); void checkBalanceChanged(const interfaces::WalletBalances& new_balances);

View File

@ -55,6 +55,7 @@ EXPECTED_CIRCULAR_DEPENDENCIES=(
"qt/bitcoingui -> qt/guiutil -> qt/bitcoingui" "qt/bitcoingui -> qt/guiutil -> qt/bitcoingui"
"qt/guiutil -> qt/optionsdialog -> qt/guiutil" "qt/guiutil -> qt/optionsdialog -> qt/guiutil"
"qt/guiutil -> qt/qvalidatedlineedit -> qt/guiutil" "qt/guiutil -> qt/qvalidatedlineedit -> qt/guiutil"
"qt/clientmodel -> qt/guiutil -> qt/walletmodel -> qt/clientmodel"
"core_io -> evo/cbtx -> evo/deterministicmns -> core_io" "core_io -> evo/cbtx -> evo/deterministicmns -> core_io"
"core_io -> evo/cbtx -> evo/simplifiedmns -> core_io" "core_io -> evo/cbtx -> evo/simplifiedmns -> core_io"
"evo/simplifiedmns -> llmq/blockprocessor -> net_processing -> evo/simplifiedmns" "evo/simplifiedmns -> llmq/blockprocessor -> net_processing -> evo/simplifiedmns"