Skip to content

Commit

Permalink
A small overhaul of the way MN list/stats UI and data are tied togeth…
Browse files Browse the repository at this point in the history
…er (#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
  • Loading branch information
UdjinM6 committed Feb 12, 2019
1 parent 90bb3ca commit fef8e5d
Show file tree
Hide file tree
Showing 10 changed files with 121 additions and 90 deletions.
1 change: 1 addition & 0 deletions src/evo/deterministicmns.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
56 changes: 27 additions & 29 deletions src/qt/clientmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@
#include "masternode-sync.h"
#include "privatesend.h"

#include "evo/deterministicmns.h"

#include <stdint.h>

#include <QDebug>
Expand All @@ -40,7 +38,6 @@ ClientModel::ClientModel(OptionsModel *_optionsModel, QObject *parent) :
QObject(parent),
optionsModel(_optionsModel),
peerTableModel(0),
cachedMasternodeCountString(""),
banTableModel(0),
pollTimer(0)
{
Expand All @@ -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();
}

Expand All @@ -81,16 +73,26 @@ int ClientModel::getNumConnections(unsigned int flags) const
return 0;
}

QString ClientModel::getMasternodeCountString() const
void ClientModel::setMasternodeList(const CDeterministicMNList& mnList)
{
LOCK(cs_mnlinst);
if (mnListCached.GetBlockHash() == mnList.GetBlockHash()) {
return;
}
mnListCached = mnList;
Q_EMIT masternodeListChanged();
}

CDeterministicMNList ClientModel::getMasternodeList() const
{
// 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);
return mnListCached;
}

void ClientModel::refreshMasternodeList()
{
LOCK(cs_mnlinst);
setMasternodeList(deterministicMNManager->GetListAtChainTip());
}

int ClientModel::getNumBlocks() const
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)
Expand Down
19 changes: 14 additions & 5 deletions src/qt/clientmodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -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>

Expand Down Expand Up @@ -53,14 +56,17 @@ class ClientModel : public QObject

//! 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;
//! Return number of transactions in the mempool
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;
Expand Down Expand Up @@ -92,18 +98,22 @@ class ClientModel : public QObject
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);
Expand All @@ -119,7 +129,6 @@ class ClientModel : public QObject

public Q_SLOTS:
void updateTimer();
void updateMnTimer();
void updateNumConnections(int numConnections);
void updateNetworkActive(bool networkActive);
void updateAlert(const QString &hash, int status);
Expand Down
72 changes: 34 additions & 38 deletions src/qt/masternodelist.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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()
Expand All @@ -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()));
}
}

Expand All @@ -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::updateDIP3ListForced()
{
updateDIP3List(true);
}

void MasternodeList::updateDIP3List()
void MasternodeList::updateDIP3List(bool fForce)
{
if (ShutdownRequested()) {
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_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;
// 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;

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;
}
if (nSecondsToWait > 0) return;

nTimeListUpdated = GetTime();
fFilterUpdatedDIP3 = false;

QString strToFilter;
Expand All @@ -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++) {
Expand All @@ -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
Expand Down Expand Up @@ -261,6 +253,10 @@ void MasternodeList::on_checkBoxMyMasternodesOnly_stateChanged(int state)

CDeterministicMNCPtr MasternodeList::GetSelectedDIP3MN()
{
if (!clientModel) {
return nullptr;
}

std::string strProTxHash;
{
LOCK(cs_dip3list);
Expand All @@ -278,7 +274,7 @@ CDeterministicMNCPtr MasternodeList::GetSelectedDIP3MN()
uint256 proTxHash;
proTxHash.SetHex(strProTxHash);

auto mnList = deterministicMNManager->GetListAtChainTip();
auto mnList = clientModel->getMasternodeList();
return mnList.GetMN(proTxHash);
}

Expand Down
20 changes: 11 additions & 9 deletions src/qt/masternodelist.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#include "sync.h"
#include "util.h"

#include <evo/deterministicmns.h>
#include "evo/deterministicmns.h"

#include <QMenu>
#include <QTimer>
Expand Down Expand Up @@ -38,20 +38,12 @@ class MasternodeList : public QWidget

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;
Expand All @@ -62,6 +54,13 @@ public Q_SLOTS:

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);
Expand All @@ -70,5 +69,8 @@ private Q_SLOTS:
void extraInfoDIP3_clicked();
void copyProTxHash_clicked();
void copyCollateralOutpoint_clicked();

void updateDIP3ListScheduled();
void updateDIP3ListForced();
};
#endif // MASTERNODELIST_H
Loading

0 comments on commit fef8e5d

Please sign in to comment.