A small overhaul of the way MN list/stats UI and data are tied together (#2696)

* Do not access wallet directly from masternodelist ui, use wallet model instead

* Don't access deterministicMNManager from UI directly, use client model instead

* Send just a general signal to UI elements when mn list has changed and let them handle it in their specific ways

* Drop mn list update timers and use signals instead

* some cleanup

* Move initial UI update to init.cpp

* Refactor getMasternodeList()

* Rename setMasternodeCount to updateMasternodeCount

* Drop legacy code in comments

* Drop NotifyMasternodeListChanged from uiInterface and use NotifyBlockTip instead
This commit is contained in:
UdjinM6 2019-02-12 22:51:03 +03:00 committed by GitHub
parent 90bb3ca2f6
commit fef8e5d45f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 122 additions and 91 deletions

View File

@ -9,6 +9,7 @@
#include "chainparams.h"
#include "core_io.h"
#include "script/standard.h"
#include "ui_interface.h"
#include "validation.h"
#include "validationinterface.h"

View File

@ -23,8 +23,6 @@
#include "masternode-sync.h"
#include "privatesend.h"
#include "evo/deterministicmns.h"
#include <stdint.h>
#include <QDebug>
@ -40,7 +38,6 @@ ClientModel::ClientModel(OptionsModel *_optionsModel, QObject *parent) :
QObject(parent),
optionsModel(_optionsModel),
peerTableModel(0),
cachedMasternodeCountString(""),
banTableModel(0),
pollTimer(0)
{
@ -52,11 +49,6 @@ ClientModel::ClientModel(OptionsModel *_optionsModel, QObject *parent) :
connect(pollTimer, SIGNAL(timeout()), this, SLOT(updateTimer()));
pollTimer->start(MODEL_UPDATE_DELAY);
pollMnTimer = new QTimer(this);
connect(pollMnTimer, SIGNAL(timeout()), this, SLOT(updateMnTimer()));
// no need to update as frequent as data for balances/txes/blocks
pollMnTimer->start(MODEL_UPDATE_DELAY * 4);
subscribeToCoreSignals();
}
@ -81,16 +73,26 @@ int ClientModel::getNumConnections(unsigned int flags) const
return 0;
}
QString ClientModel::getMasternodeCountString() const
void ClientModel::setMasternodeList(const CDeterministicMNList& mnList)
{
// return tr("Total: %1 (PS compatible: %2 / Enabled: %3) (IPv4: %4, IPv6: %5, TOR: %6)").arg(QString::number((int)mnodeman.size()))
auto mnList = deterministicMNManager->GetListAtChainTip();
return tr("Total: %1 (Enabled: %2)")
.arg(QString::number((int)mnList.GetAllMNsCount()))
.arg(QString::number((int)mnList.GetValidMNsCount()));
// .arg(QString::number((int)mnodeman.CountByIP(NET_IPV4)))
// .arg(QString::number((int)mnodeman.CountByIP(NET_IPV6)))
// .arg(QString::number((int)mnodeman.CountByIP(NET_TOR)));
LOCK(cs_mnlinst);
if (mnListCached.GetBlockHash() == mnList.GetBlockHash()) {
return;
}
mnListCached = mnList;
Q_EMIT masternodeListChanged();
}
CDeterministicMNList ClientModel::getMasternodeList() const
{
LOCK(cs_mnlinst);
return mnListCached;
}
void ClientModel::refreshMasternodeList()
{
LOCK(cs_mnlinst);
setMasternodeList(deterministicMNManager->GetListAtChainTip());
}
int ClientModel::getNumBlocks() const
@ -178,18 +180,6 @@ void ClientModel::updateTimer()
Q_EMIT bytesChanged(getTotalBytesRecv(), getTotalBytesSent());
}
void ClientModel::updateMnTimer()
{
QString newMasternodeCountString = getMasternodeCountString();
if (cachedMasternodeCountString != newMasternodeCountString)
{
cachedMasternodeCountString = newMasternodeCountString;
Q_EMIT strMasternodesChanged(cachedMasternodeCountString);
}
}
void ClientModel::updateNumConnections(int numConnections)
{
Q_EMIT numConnectionsChanged(numConnections);
@ -361,6 +351,14 @@ static void BlockTipChanged(ClientModel *clientmodel, bool initialSync, const CB
Q_ARG(bool, fHeader));
nLastUpdateNotification = now;
}
static int64_t nLastMasternodeUpdateNotification = 0;
// if we are in-sync, update the UI regardless of last update time
// no need to refresh masternode list/stats as often as blocks etc.
if (!initialSync || now - nLastMasternodeUpdateNotification > MODEL_UPDATE_DELAY*4*5) {
clientmodel->refreshMasternodeList();
nLastMasternodeUpdateNotification = now;
}
}
static void NotifyAdditionalDataSyncProgressChanged(ClientModel *clientmodel, double nSyncProgress)

View File

@ -6,6 +6,9 @@
#ifndef BITCOIN_QT_CLIENTMODEL_H
#define BITCOIN_QT_CLIENTMODEL_H
#include "evo/deterministicmns.h"
#include "sync.h"
#include <QObject>
#include <QDateTime>
@ -53,7 +56,6 @@ public:
//! Return number of connections, default is in- and outbound (total)
int getNumConnections(unsigned int flags = CONNECTIONS_ALL) const;
QString getMasternodeCountString() const;
int getNumBlocks() const;
int getHeaderTipHeight() const;
int64_t getHeaderTipTime() const;
@ -61,6 +63,10 @@ public:
long getMempoolSize() const;
//! Return the dynamic memory usage of the mempool
size_t getMempoolDynamicUsage() const;
void setMasternodeList(const CDeterministicMNList& mnList);
CDeterministicMNList getMasternodeList() const;
void refreshMasternodeList();
quint64 getTotalBytesRecv() const;
quint64 getTotalBytesSent() const;
@ -92,18 +98,22 @@ public:
private:
OptionsModel *optionsModel;
PeerTableModel *peerTableModel;
QString cachedMasternodeCountString;
BanTableModel *banTableModel;
QTimer *pollTimer;
QTimer *pollMnTimer;
// The cache for mn list is not technically needed because CDeterministicMNManager
// caches it internally for recent blocks but it's not enough to get consistent
// representation of the list in UI during initial sync/reindex, so we cache it here too.
mutable CCriticalSection cs_mnlinst; // protects mnListCached
CDeterministicMNList mnListCached;
void subscribeToCoreSignals();
void unsubscribeFromCoreSignals();
Q_SIGNALS:
void numConnectionsChanged(int count);
void strMasternodesChanged(const QString &strMasternodes);
void masternodeListChanged() const;
void numBlocksChanged(int count, const QDateTime& blockDate, double nVerificationProgress, bool header);
void additionalDataSyncProgressChanged(double nSyncProgress);
void mempoolSizeChanged(long count, size_t mempoolSizeInBytes);
@ -119,7 +129,6 @@ Q_SIGNALS:
public Q_SLOTS:
void updateTimer();
void updateMnTimer();
void updateNumConnections(int numConnections);
void updateNetworkActive(bool networkActive);
void updateAlert(const QString &hash, int status);

View File

@ -34,7 +34,9 @@ MasternodeList::MasternodeList(const PlatformStyle* platformStyle, QWidget* pare
QWidget(parent),
ui(new Ui::MasternodeList),
clientModel(0),
walletModel(0)
walletModel(0),
fFilterUpdatedDIP3(true),
nTimeFilterUpdatedDIP3(0)
{
ui->setupUi(this);
@ -74,12 +76,8 @@ MasternodeList::MasternodeList(const PlatformStyle* platformStyle, QWidget* pare
connect(copyCollateralOutpointAction, SIGNAL(triggered()), this, SLOT(copyCollateralOutpoint_clicked()));
timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(updateDIP3List()));
connect(timer, SIGNAL(timeout()), this, SLOT(updateDIP3ListScheduled()));
timer->start(1000);
fFilterUpdatedDIP3 = false;
nTimeFilterUpdatedDIP3 = GetTime();
updateDIP3List();
}
MasternodeList::~MasternodeList()
@ -92,7 +90,7 @@ void MasternodeList::setClientModel(ClientModel* model)
this->clientModel = model;
if (model) {
// try to update list when masternode count changes
connect(clientModel, SIGNAL(strMasternodesChanged(QString)), this, SLOT(updateNodeList()));
connect(clientModel, SIGNAL(masternodeListChanged()), this, SLOT(updateDIP3ListForced()));
}
}
@ -107,40 +105,36 @@ void MasternodeList::showContextMenuDIP3(const QPoint& point)
if (item) contextMenuDIP3->exec(QCursor::pos());
}
static bool CheckWalletOwnsScript(const CScript& script)
void MasternodeList::updateDIP3ListScheduled()
{
CTxDestination dest;
if (ExtractDestination(script, dest)) {
if ((boost::get<CKeyID>(&dest) && pwalletMain->HaveKey(*boost::get<CKeyID>(&dest))) || (boost::get<CScriptID>(&dest) && pwalletMain->HaveCScript(*boost::get<CScriptID>(&dest)))) {
return true;
}
}
return false;
updateDIP3List(false);
}
void MasternodeList::updateDIP3List()
void MasternodeList::updateDIP3ListForced()
{
if (ShutdownRequested()) {
updateDIP3List(true);
}
void MasternodeList::updateDIP3List(bool fForce)
{
if (!clientModel || ShutdownRequested()) {
return;
}
TRY_LOCK(cs_dip3list, fLockAcquired);
if (!fLockAcquired) return;
static int64_t nTimeListUpdated = GetTime();
// To prevent high cpu usage update only once in MASTERNODELIST_FILTER_COOLDOWN_SECONDS seconds
// after filter was last changed unless we want to force the update.
if (!fForce) {
if (!fFilterUpdatedDIP3) return;
// to prevent high cpu usage update only once in MASTERNODELIST_UPDATE_SECONDS seconds
// or MASTERNODELIST_FILTER_COOLDOWN_SECONDS seconds after filter was last changed
int64_t nSecondsToWait = fFilterUpdatedDIP3
? nTimeFilterUpdatedDIP3 - GetTime() + MASTERNODELIST_FILTER_COOLDOWN_SECONDS
: nTimeListUpdated - GetTime() + MASTERNODELIST_UPDATE_SECONDS;
if (fFilterUpdatedDIP3) {
int64_t nSecondsToWait = nTimeFilterUpdatedDIP3 - GetTime() + MASTERNODELIST_FILTER_COOLDOWN_SECONDS;
ui->countLabelDIP3->setText(QString::fromStdString(strprintf("Please wait... %d", nSecondsToWait)));
}
if (nSecondsToWait > 0) return;
nTimeListUpdated = GetTime();
if (nSecondsToWait > 0) return;
}
fFilterUpdatedDIP3 = false;
QString strToFilter;
@ -149,7 +143,7 @@ void MasternodeList::updateDIP3List()
ui->tableWidgetMasternodesDIP3->clearContents();
ui->tableWidgetMasternodesDIP3->setRowCount(0);
auto mnList = deterministicMNManager->GetListAtChainTip();
auto mnList = clientModel->getMasternodeList();
auto projectedPayees = mnList.GetProjectedMNPayees(mnList.GetValidMNsCount());
std::map<uint256, int> nextPayments;
for (size_t i = 0; i < projectedPayees.size(); i++) {
@ -158,23 +152,21 @@ void MasternodeList::updateDIP3List()
}
std::set<COutPoint> setOutpts;
if (pwalletMain && ui->checkBoxMyMasternodesOnly->isChecked()) {
LOCK(pwalletMain->cs_wallet);
if (walletModel && ui->checkBoxMyMasternodesOnly->isChecked()) {
std::vector<COutPoint> vOutpts;
pwalletMain->ListProTxCoins(vOutpts);
walletModel->listProTxCoins(vOutpts);
for (const auto& outpt : vOutpts) {
setOutpts.emplace(outpt);
}
}
mnList.ForEachMN(false, [&](const CDeterministicMNCPtr& dmn) {
if (pwalletMain && ui->checkBoxMyMasternodesOnly->isChecked()) {
LOCK(pwalletMain->cs_wallet);
if (walletModel && ui->checkBoxMyMasternodesOnly->isChecked()) {
bool fMyMasternode = setOutpts.count(dmn->collateralOutpoint) ||
pwalletMain->HaveKey(dmn->pdmnState->keyIDOwner) ||
pwalletMain->HaveKey(dmn->pdmnState->keyIDVoting) ||
CheckWalletOwnsScript(dmn->pdmnState->scriptPayout) ||
CheckWalletOwnsScript(dmn->pdmnState->scriptOperatorPayout);
walletModel->havePrivKey(dmn->pdmnState->keyIDOwner) ||
walletModel->havePrivKey(dmn->pdmnState->keyIDVoting) ||
walletModel->havePrivKey(dmn->pdmnState->scriptPayout) ||
walletModel->havePrivKey(dmn->pdmnState->scriptOperatorPayout);
if (!fMyMasternode) return;
}
// populate list
@ -261,6 +253,10 @@ void MasternodeList::on_checkBoxMyMasternodesOnly_stateChanged(int state)
CDeterministicMNCPtr MasternodeList::GetSelectedDIP3MN()
{
if (!clientModel) {
return nullptr;
}
std::string strProTxHash;
{
LOCK(cs_dip3list);
@ -278,7 +274,7 @@ CDeterministicMNCPtr MasternodeList::GetSelectedDIP3MN()
uint256 proTxHash;
proTxHash.SetHex(strProTxHash);
auto mnList = deterministicMNManager->GetListAtChainTip();
auto mnList = clientModel->getMasternodeList();
return mnList.GetMN(proTxHash);
}

View File

@ -6,7 +6,7 @@
#include "sync.h"
#include "util.h"
#include <evo/deterministicmns.h>
#include "evo/deterministicmns.h"
#include <QMenu>
#include <QTimer>
@ -38,20 +38,12 @@ public:
void setClientModel(ClientModel* clientModel);
void setWalletModel(WalletModel* walletModel);
CDeterministicMNCPtr GetSelectedDIP3MN();
private:
QMenu* contextMenuDIP3;
int64_t nTimeFilterUpdatedDIP3;
bool fFilterUpdatedDIP3;
public Q_SLOTS:
void updateDIP3List();
Q_SIGNALS:
void doubleClicked(const QModelIndex&);
private:
QTimer* timer;
Ui::MasternodeList* ui;
ClientModel* clientModel;
@ -62,6 +54,13 @@ private:
QString strCurrentFilterDIP3;
CDeterministicMNCPtr GetSelectedDIP3MN();
void updateDIP3List(bool fForce);
Q_SIGNALS:
void doubleClicked(const QModelIndex&);
private Q_SLOTS:
void showContextMenuDIP3(const QPoint&);
void on_filterLineEditDIP3_textChanged(const QString& strFilterIn);
@ -70,5 +69,8 @@ private Q_SLOTS:
void extraInfoDIP3_clicked();
void copyProTxHash_clicked();
void copyCollateralOutpoint_clicked();
void updateDIP3ListScheduled();
void updateDIP3ListForced();
};
#endif // MASTERNODELIST_H

View File

@ -556,8 +556,8 @@ void RPCConsole::setClientModel(ClientModel *model)
updateNetworkState();
connect(model, SIGNAL(networkActiveChanged(bool)), this, SLOT(setNetworkActive(bool)));
setMasternodeCount(model->getMasternodeCountString());
connect(model, SIGNAL(strMasternodesChanged(QString)), this, SLOT(setMasternodeCount(QString)));
connect(model, SIGNAL(masternodeListChanged()), this, SLOT(updateMasternodeCount()));
clientModel->refreshMasternodeList();
updateTrafficStats(model->getTotalBytesRecv(), model->getTotalBytesSent());
connect(model, SIGNAL(bytesChanged(quint64,quint64)), this, SLOT(updateTrafficStats(quint64, quint64)));
@ -884,9 +884,16 @@ void RPCConsole::setNumBlocks(int count, const QDateTime& blockDate, double nVer
}
}
void RPCConsole::setMasternodeCount(const QString &strMasternodes)
void RPCConsole::updateMasternodeCount()
{
ui->masternodeCount->setText(strMasternodes);
if (!clientModel) {
return;
}
auto mnList = clientModel->getMasternodeList();
QString strMasternodeCount = tr("Total: %1 (Enabled: %2)")
.arg(QString::number(mnList.GetAllMNsCount()))
.arg(QString::number(mnList.GetValidMNsCount()));
ui->masternodeCount->setText(strMasternodeCount);
}
void RPCConsole::setMempoolSize(long numberOfTxs, size_t dynUsage)

View File

@ -105,8 +105,8 @@ public Q_SLOTS:
void setNumConnections(int count);
/** Set network state shown in the UI */
void setNetworkActive(bool networkActive);
/** Set number of masternodes shown in the UI */
void setMasternodeCount(const QString &strMasternodes);
/** Update number of masternodes shown in the UI */
void updateMasternodeCount();
/** Set number of blocks and last block date shown in the UI */
void setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool headers);
/** Set size (number of transactions and memory usage) of the mempool in the UI */

View File

@ -645,6 +645,17 @@ bool WalletModel::havePrivKey(const CKeyID &address) const
return wallet->HaveKey(address);
}
bool WalletModel::havePrivKey(const CScript& script) const
{
CTxDestination dest;
if (ExtractDestination(script, dest)) {
if ((boost::get<CKeyID>(&dest) && wallet->HaveKey(*boost::get<CKeyID>(&dest))) || (boost::get<CScriptID>(&dest) && wallet->HaveCScript(*boost::get<CScriptID>(&dest)))) {
return true;
}
}
return false;
}
bool WalletModel::getPrivKey(const CKeyID &address, CKey& vchPrivKeyOut) const
{
return wallet->GetKey(address, vchPrivKeyOut);
@ -732,6 +743,12 @@ void WalletModel::listLockedCoins(std::vector<COutPoint>& vOutpts)
wallet->ListLockedCoins(vOutpts);
}
void WalletModel::listProTxCoins(std::vector<COutPoint>& vOutpts)
{
LOCK2(cs_main, wallet->cs_wallet);
wallet->ListProTxCoins(vOutpts);
}
void WalletModel::loadReceiveRequests(std::vector<std::string>& vReceiveRequests)
{
LOCK(wallet->cs_wallet);

View File

@ -202,6 +202,7 @@ public:
bool getPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const;
bool havePrivKey(const CKeyID &address) const;
bool havePrivKey(const CScript& script) const;
bool getPrivKey(const CKeyID &address, CKey& vchPrivKeyOut) const;
void getOutputs(const std::vector<COutPoint>& vOutpoints, std::vector<COutput>& vOutputs);
bool isSpent(const COutPoint& outpoint) const;
@ -212,6 +213,8 @@ public:
void unlockCoin(COutPoint& output);
void listLockedCoins(std::vector<COutPoint>& vOutpts);
void listProTxCoins(std::vector<COutPoint>& vOutpts);
void loadReceiveRequests(std::vector<std::string>& vReceiveRequests);
bool saveReceiveRequest(const std::string &sAddress, const int64_t nId, const std::string &sRequest);

View File

@ -16,6 +16,7 @@ class CBasicKeyStore;
class CWallet;
class uint256;
class CBlockIndex;
class CDeterministicMNList;
/** General change type (added, updated, removed). */
enum ChangeType
@ -88,9 +89,6 @@ public:
/** Network activity state changed. */
boost::signals2::signal<void (bool networkActive)> NotifyNetworkActiveChanged;
/** Number of masternodes changed. */
boost::signals2::signal<void (int newNumMasternodes)> NotifyStrMasternodeCountChanged;
/**
* New, updated or cancelled alert.
* @note called with lock cs_mapAlerts held.