diff --git a/src/qt/forms/masternodelist.ui b/src/qt/forms/masternodelist.ui index 992d6c72d..a206094f2 100644 --- a/src/qt/forms/masternodelist.ui +++ b/src/qt/forms/masternodelist.ui @@ -217,7 +217,55 @@ All Masternodes - + + + + 0 + + + + + Filter List: + + + + + + + Filter masternode list + + + + + + + Qt::Horizontal + + + + 10 + 20 + + + + + + + + Node Count: + + + + + + + 0 + + + + + + QAbstractItemView::NoEditTriggers @@ -270,26 +318,46 @@ - + + + <html><head/><body><p>Note: This list represents the legacy and non-deterministic masternode list. It is only active as long as DIP3 has not been fully activated. After SPORK15 activation, this list will be empty.</p></body></html> + + + Qt::AutoText + + + true + + + + + + + + DIP3 Masternodes + + + + 0 - + Filter List: - + Filter masternode list - + Qt::Horizontal @@ -302,14 +370,14 @@ - + Node Count: - + 0 @@ -317,6 +385,81 @@ + + + + QAbstractItemView::NoEditTriggers + + + true + + + QAbstractItemView::NoSelection + + + QAbstractItemView::SelectRows + + + true + + + true + + + + Address + + + + + Status + + + + + PoSe Score + + + + + Registered + + + + + Last Paid + + + + + Next Payment + + + + + Payee + + + + + Operator Reward + + + + + + + + <html><head/><body><p>Note: This list is not active yet and only for informational purposes. The network is still running in compatibility mode, which means that the non-deterministic masternode list is still active. Only after SPORK15 activation, this list will become the active one. Also, the values in the payment related fields are not used at the moment, but still updated for every block. Please ignore this until SPORK15 activation.</p></body></html> + + + Qt::AutoText + + + true + + + diff --git a/src/qt/masternodelist.cpp b/src/qt/masternodelist.cpp index 2e9cd8a97..f3cf5ae7d 100644 --- a/src/qt/masternodelist.cpp +++ b/src/qt/masternodelist.cpp @@ -45,6 +45,13 @@ MasternodeList::MasternodeList(const PlatformStyle* platformStyle, QWidget* pare int columnActiveWidth = 130; int columnLastSeenWidth = 130; + int columnPoSeScoreWidth = 80; + int columnRegisteredWidth = 80; + int columnLastPaidWidth = 80; + int columnNextPaymentWidth = 100; + int columnPayeeWidth = 130; + int columnOperatorRewardWidth = 130; + ui->tableWidgetMyMasternodes->setColumnWidth(0, columnAliasWidth); ui->tableWidgetMyMasternodes->setColumnWidth(1, columnAddressWidth); ui->tableWidgetMyMasternodes->setColumnWidth(2, columnProtocolWidth); @@ -58,6 +65,15 @@ MasternodeList::MasternodeList(const PlatformStyle* platformStyle, QWidget* pare ui->tableWidgetMasternodes->setColumnWidth(3, columnActiveWidth); ui->tableWidgetMasternodes->setColumnWidth(4, columnLastSeenWidth); + ui->tableWidgetMasternodesDIP3->setColumnWidth(0, columnAddressWidth); + ui->tableWidgetMasternodesDIP3->setColumnWidth(1, columnStatusWidth); + ui->tableWidgetMasternodesDIP3->setColumnWidth(2, columnPoSeScoreWidth); + ui->tableWidgetMasternodesDIP3->setColumnWidth(3, columnRegisteredWidth); + ui->tableWidgetMasternodesDIP3->setColumnWidth(4, columnLastPaidWidth); + ui->tableWidgetMasternodesDIP3->setColumnWidth(5, columnNextPaymentWidth); + ui->tableWidgetMasternodesDIP3->setColumnWidth(6, columnPayeeWidth); + ui->tableWidgetMasternodesDIP3->setColumnWidth(7, columnOperatorRewardWidth); + ui->tableWidgetMyMasternodes->setContextMenuPolicy(Qt::CustomContextMenu); QAction* startAliasAction = new QAction(tr("Start alias"), this); @@ -70,11 +86,15 @@ MasternodeList::MasternodeList(const PlatformStyle* platformStyle, QWidget* pare timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(updateNodeList())); connect(timer, SIGNAL(timeout()), this, SLOT(updateMyNodeList())); + connect(timer, SIGNAL(timeout()), this, SLOT(updateDIP3List())); timer->start(1000); fFilterUpdated = false; + fFilterUpdatedDIP3 = false; nTimeFilterUpdated = GetTime(); + nTimeFilterUpdatedDIP3 = GetTime(); updateNodeList(); + updateDIP3List(); } MasternodeList::~MasternodeList() @@ -288,9 +308,16 @@ void MasternodeList::updateNodeList() ui->tableWidgetMasternodes->setSortingEnabled(false); ui->tableWidgetMasternodes->clearContents(); ui->tableWidgetMasternodes->setRowCount(0); - std::map mapMasternodes = mnodeman.GetFullMasternodeMap(); + + if (deterministicMNManager->IsDeterministicMNsSporkActive()) { + ui->countLabel->setText(QString::number(0)); + return; + } + int offsetFromUtc = GetOffsetFromUtc(); + std::map mapMasternodes = mnodeman.GetFullMasternodeMap(); + for (const auto& mnpair : mapMasternodes) { CMasternode mn = mnpair.second; // populate list @@ -325,6 +352,110 @@ void MasternodeList::updateNodeList() ui->tableWidgetMasternodes->setSortingEnabled(true); } +void MasternodeList::updateDIP3List() +{ + 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; + + if (fFilterUpdatedDIP3) { + ui->countLabel->setText(QString::fromStdString(strprintf("Please wait... %d", nSecondsToWait))); + } + if (nSecondsToWait > 0) return; + + nTimeListUpdated = GetTime(); + fFilterUpdatedDIP3 = false; + + QString strToFilter; + ui->countLabelDIP3->setText("Updating..."); + ui->tableWidgetMasternodesDIP3->setSortingEnabled(false); + ui->tableWidgetMasternodesDIP3->clearContents(); + ui->tableWidgetMasternodesDIP3->setRowCount(0); + + if (deterministicMNManager->IsDeterministicMNsSporkActive()) { + ui->dip3NoteLabel->setVisible(false); + } + + auto mnList = deterministicMNManager->GetListAtChainTip(); + auto projectedPayees = mnList.GetProjectedMNPayees(mnList.GetValidMNsCount()); + std::map nextPayments; + for (size_t i = 0; i < projectedPayees.size(); i++) { + const auto& dmn = projectedPayees[i]; + nextPayments.emplace(dmn->proTxHash, mnList.GetHeight() + (int)i + 1); + } + + mnList.ForEachMN(false, [&](const CDeterministicMNCPtr& dmn) { + // populate list + // Address, Protocol, Status, Active Seconds, Last Seen, Pub Key + QTableWidgetItem* addressItem = new QTableWidgetItem(QString::fromStdString(dmn->pdmnState->addr.ToString())); + QTableWidgetItem* statusItem = new QTableWidgetItem(mnList.IsMNValid(dmn) ? tr("ENABLED") : (mnList.IsMNPoSeBanned(dmn) ? tr("POSE_BANNED") : tr("UNKNOWN"))); + QTableWidgetItem* PoSeScoreItem = new QTableWidgetItem(QString::number(dmn->pdmnState->nPoSePenalty)); + QTableWidgetItem* registeredItem = new QTableWidgetItem(QString::number(dmn->pdmnState->nRegisteredHeight)); + QTableWidgetItem* lastPaidItem = new QTableWidgetItem(QString::number(dmn->pdmnState->nLastPaidHeight)); + QTableWidgetItem* nextPaymentItem = new QTableWidgetItem(nextPayments.count(dmn->proTxHash) ? QString::number(nextPayments[dmn->proTxHash]) : tr("UNKNOWN")); + + CTxDestination payeeDest; + QString payeeStr; + if (ExtractDestination(dmn->pdmnState->scriptPayout, payeeDest)) { + payeeStr = QString::fromStdString(CBitcoinAddress(payeeDest).ToString()); + } else { + payeeStr = tr("UNKNOWN"); + } + QTableWidgetItem* payeeItem = new QTableWidgetItem(payeeStr); + + QString operatorRewardStr; + if (dmn->nOperatorReward) { + operatorRewardStr += QString::number(dmn->nOperatorReward / 100.0, 'f', 2) + "%"; + + if (dmn->pdmnState->scriptOperatorPayout != CScript()) { + CTxDestination dest; + if (ExtractDestination(dmn->pdmnState->scriptOperatorPayout, dest)) { + operatorRewardStr += tr(" to %1").arg(QString::fromStdString(CBitcoinAddress(payeeDest).ToString())); + } else { + operatorRewardStr += tr(" to UNKNOWN"); + } + } else { + operatorRewardStr += tr(" but not claimed"); + } + } else { + operatorRewardStr = tr("NONE"); + } + QTableWidgetItem* operatorRewardItem = new QTableWidgetItem(operatorRewardStr); + + if (strCurrentFilterDIP3 != "") { + strToFilter = addressItem->text() + " " + + statusItem->text() + " " + + PoSeScoreItem->text() + " " + + registeredItem->text() + " " + + lastPaidItem->text() + " " + + nextPaymentItem->text() + " " + + payeeItem->text() + " " + + operatorRewardItem->text(); + if (!strToFilter.contains(strCurrentFilterDIP3)) return; + } + + ui->tableWidgetMasternodesDIP3->insertRow(0); + ui->tableWidgetMasternodesDIP3->setItem(0, 0, addressItem); + ui->tableWidgetMasternodesDIP3->setItem(0, 1, statusItem); + ui->tableWidgetMasternodesDIP3->setItem(0, 2, PoSeScoreItem); + ui->tableWidgetMasternodesDIP3->setItem(0, 3, registeredItem); + ui->tableWidgetMasternodesDIP3->setItem(0, 4, lastPaidItem); + ui->tableWidgetMasternodesDIP3->setItem(0, 5, nextPaymentItem); + ui->tableWidgetMasternodesDIP3->setItem(0, 6, payeeItem); + ui->tableWidgetMasternodesDIP3->setItem(0, 7, operatorRewardItem); + }); + + ui->countLabelDIP3->setText(QString::number(ui->tableWidgetMasternodesDIP3->rowCount())); + ui->tableWidgetMasternodesDIP3->setSortingEnabled(true); +} + void MasternodeList::on_filterLineEdit_textChanged(const QString& strFilterIn) { strCurrentFilter = strFilterIn; @@ -333,6 +464,14 @@ void MasternodeList::on_filterLineEdit_textChanged(const QString& strFilterIn) ui->countLabel->setText(QString::fromStdString(strprintf("Please wait... %d", MASTERNODELIST_FILTER_COOLDOWN_SECONDS))); } +void MasternodeList::on_filterLineEditDIP3_textChanged(const QString& strFilterIn) +{ + strCurrentFilterDIP3 = strFilterIn; + nTimeFilterUpdatedDIP3 = GetTime(); + fFilterUpdatedDIP3 = true; + ui->countLabelDIP3->setText(QString::fromStdString(strprintf("Please wait... %d", MASTERNODELIST_FILTER_COOLDOWN_SECONDS))); +} + void MasternodeList::on_startButton_clicked() { std::string strAlias; diff --git a/src/qt/masternodelist.h b/src/qt/masternodelist.h index 34eb3d0b8..d51a40374 100644 --- a/src/qt/masternodelist.h +++ b/src/qt/masternodelist.h @@ -44,12 +44,15 @@ public: private: QMenu* contextMenu; int64_t nTimeFilterUpdated; + int64_t nTimeFilterUpdatedDIP3; bool fFilterUpdated; + bool fFilterUpdatedDIP3; public Q_SLOTS: void updateMyMasternodeInfo(QString strAlias, QString strAddr, const COutPoint& outpoint); void updateMyNodeList(bool fForce = false); void updateNodeList(); + void updateDIP3List(); Q_SIGNALS: void doubleClicked(const QModelIndex&); @@ -66,11 +69,16 @@ private: // Protects tableWidgetMyMasternodes CCriticalSection cs_mymnlist; + // Protects tableWidgetMasternodesDIP3 + CCriticalSection cs_dip3list; + QString strCurrentFilter; + QString strCurrentFilterDIP3; private Q_SLOTS: void showContextMenu(const QPoint&); void on_filterLineEdit_textChanged(const QString& strFilterIn); + void on_filterLineEditDIP3_textChanged(const QString& strFilterIn); void on_QRButton_clicked(); void on_startButton_clicked(); void on_startAllButton_clicked();