// Copyright (c) 2014-2016 The Dash Developers // Copyright (c) 2016-2019 The PIVX developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "masternodelist.h" #include "ui_masternodelist.h" #include "activemasternode.h" #include "clientmodel.h" #include "guiutil.h" #include "init.h" #include "masternode-sync.h" #include "masternodeconfig.h" #include "masternodeman.h" #include "sync.h" #include "wallet/wallet.h" #include "walletmodel.h" #include "askpassphrasedialog.h" #include #include CCriticalSection cs_masternodes; MasternodeList::MasternodeList(QWidget* parent) : QWidget(parent), ui(new Ui::MasternodeList), clientModel(0), walletModel(0) { ui->setupUi(this); ui->startButton->setEnabled(false); int columnAliasWidth = 100; int columnAddressWidth = 200; int columnProtocolWidth = 60; int columnStatusWidth = 80; int columnActiveWidth = 130; int columnLastSeenWidth = 130; ui->tableWidgetMyMasternodes->setAlternatingRowColors(true); ui->tableWidgetMyMasternodes->setColumnWidth(0, columnAliasWidth); ui->tableWidgetMyMasternodes->setColumnWidth(1, columnAddressWidth); ui->tableWidgetMyMasternodes->setColumnWidth(2, columnProtocolWidth); ui->tableWidgetMyMasternodes->setColumnWidth(3, columnStatusWidth); ui->tableWidgetMyMasternodes->setColumnWidth(4, columnActiveWidth); ui->tableWidgetMyMasternodes->setColumnWidth(5, columnLastSeenWidth); ui->tableWidgetMyMasternodes->setContextMenuPolicy(Qt::CustomContextMenu); QAction* startAliasAction = new QAction(tr("Start alias"), this); contextMenu = new QMenu(); contextMenu->addAction(startAliasAction); connect(ui->tableWidgetMyMasternodes, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showContextMenu(const QPoint&))); connect(startAliasAction, SIGNAL(triggered()), this, SLOT(on_startButton_clicked())); timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(updateMyNodeList())); timer->start(1000); // Fill MN list fFilterUpdated = true; nTimeFilterUpdated = GetTime(); } MasternodeList::~MasternodeList() { delete ui; } void MasternodeList::setClientModel(ClientModel* model) { this->clientModel = model; } void MasternodeList::setWalletModel(WalletModel* model) { this->walletModel = model; } void MasternodeList::showContextMenu(const QPoint& point) { QTableWidgetItem* item = ui->tableWidgetMyMasternodes->itemAt(point); if (item) contextMenu->exec(QCursor::pos()); } void MasternodeList::StartAlias(std::string strAlias) { std::string strStatusHtml; strStatusHtml += "
Alias: " + strAlias; for (CMasternodeConfig::CMasternodeEntry mne : masternodeConfig.getEntries()) { if (mne.getAlias() == strAlias) { std::string strError; CMasternodeBroadcast mnb; bool fSuccess = CMasternodeBroadcast::Create(mne.getIp(), mne.getPrivKey(), mne.getTxHash(), mne.getOutputIndex(), strError, mnb); if (fSuccess) { strStatusHtml += "
Successfully started masternode."; mnodeman.UpdateMasternodeList(mnb); mnb.Relay(); } else { strStatusHtml += "
Failed to start masternode.
Error: " + strError; } break; } } strStatusHtml += "
"; QMessageBox msg; msg.setText(QString::fromStdString(strStatusHtml)); msg.exec(); updateMyNodeList(true); } void MasternodeList::StartAll(std::string strCommand) { int nCountSuccessful = 0; int nCountFailed = 0; std::string strFailedHtml; for (CMasternodeConfig::CMasternodeEntry mne : masternodeConfig.getEntries()) { std::string strError; CMasternodeBroadcast mnb; int nIndex; if(!mne.castOutputIndex(nIndex)) continue; CTxIn txin = CTxIn(uint256S(mne.getTxHash()), uint32_t(nIndex)); CMasternode* pmn = mnodeman.Find(txin); if (strCommand == "start-missing" && pmn) continue; bool fSuccess = CMasternodeBroadcast::Create(mne.getIp(), mne.getPrivKey(), mne.getTxHash(), mne.getOutputIndex(), strError, mnb); if (fSuccess) { nCountSuccessful++; mnodeman.UpdateMasternodeList(mnb); mnb.Relay(); } else { nCountFailed++; strFailedHtml += "\nFailed to start " + mne.getAlias() + ". Error: " + strError; } } pwalletMain->Lock(); std::string returnObj; returnObj = strprintf("Successfully started %d masternodes, failed to start %d, total %d", nCountSuccessful, nCountFailed, nCountFailed + nCountSuccessful); if (nCountFailed > 0) { returnObj += strFailedHtml; } QMessageBox msg; msg.setText(QString::fromStdString(returnObj)); msg.exec(); updateMyNodeList(true); } void MasternodeList::updateMyMasternodeInfo(QString strAlias, QString strAddr, CMasternode* pmn) { LOCK(cs_mnlistupdate); bool fOldRowFound = false; int nNewRow = 0; for (int i = 0; i < ui->tableWidgetMyMasternodes->rowCount(); i++) { if (ui->tableWidgetMyMasternodes->item(i, 0)->text() == strAlias) { fOldRowFound = true; nNewRow = i; break; } } if (nNewRow == 0 && !fOldRowFound) { nNewRow = ui->tableWidgetMyMasternodes->rowCount(); ui->tableWidgetMyMasternodes->insertRow(nNewRow); } QTableWidgetItem* aliasItem = new QTableWidgetItem(strAlias); QTableWidgetItem* addrItem = new QTableWidgetItem(pmn ? QString::fromStdString(pmn->addr.ToString()) : strAddr); QTableWidgetItem* protocolItem = new QTableWidgetItem(QString::number(pmn ? pmn->protocolVersion : -1)); QTableWidgetItem* statusItem = new QTableWidgetItem(QString::fromStdString(pmn ? pmn->GetStatus() : "MISSING")); GUIUtil::DHMSTableWidgetItem* activeSecondsItem = new GUIUtil::DHMSTableWidgetItem(pmn ? (pmn->lastPing.sigTime - pmn->sigTime) : 0); QTableWidgetItem* lastSeenItem = new QTableWidgetItem(QString::fromStdString(DateTimeStrFormat("%Y-%m-%d %H:%M", pmn ? pmn->lastPing.sigTime : 0))); QTableWidgetItem* pubkeyItem = new QTableWidgetItem(QString::fromStdString(pmn ? CBitcoinAddress(pmn->pubKeyCollateralAddress.GetID()).ToString() : "")); ui->tableWidgetMyMasternodes->setItem(nNewRow, 0, aliasItem); ui->tableWidgetMyMasternodes->setItem(nNewRow, 1, addrItem); ui->tableWidgetMyMasternodes->setItem(nNewRow, 2, protocolItem); ui->tableWidgetMyMasternodes->setItem(nNewRow, 3, statusItem); ui->tableWidgetMyMasternodes->setItem(nNewRow, 4, activeSecondsItem); ui->tableWidgetMyMasternodes->setItem(nNewRow, 5, lastSeenItem); ui->tableWidgetMyMasternodes->setItem(nNewRow, 6, pubkeyItem); } void MasternodeList::updateMyNodeList(bool fForce) { static int64_t nTimeMyListUpdated = 0; // automatically update my masternode list only once in MY_MASTERNODELIST_UPDATE_SECONDS seconds, // this update still can be triggered manually at any time via button click int64_t nSecondsTillUpdate = nTimeMyListUpdated + MY_MASTERNODELIST_UPDATE_SECONDS - GetTime(); ui->secondsLabel->setText(QString::number(nSecondsTillUpdate)); if (nSecondsTillUpdate > 0 && !fForce) return; nTimeMyListUpdated = GetTime(); ui->tableWidgetMyMasternodes->setSortingEnabled(false); for (CMasternodeConfig::CMasternodeEntry mne : masternodeConfig.getEntries()) { int nIndex; if(!mne.castOutputIndex(nIndex)) continue; CTxIn txin = CTxIn(uint256S(mne.getTxHash()), uint32_t(nIndex)); CMasternode* pmn = mnodeman.Find(txin); updateMyMasternodeInfo(QString::fromStdString(mne.getAlias()), QString::fromStdString(mne.getIp()), pmn); } ui->tableWidgetMyMasternodes->setSortingEnabled(true); // reset "timer" ui->secondsLabel->setText("0"); } void MasternodeList::on_startButton_clicked() { // Find selected node alias QItemSelectionModel* selectionModel = ui->tableWidgetMyMasternodes->selectionModel(); QModelIndexList selected = selectionModel->selectedRows(); if (selected.count() == 0) return; QModelIndex index = selected.at(0); int nSelectedRow = index.row(); std::string strAlias = ui->tableWidgetMyMasternodes->item(nSelectedRow, 0)->text().toStdString(); // Display message box QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm masternode start"), tr("Are you sure you want to start masternode %1?").arg(QString::fromStdString(strAlias)), QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel); if (retval != QMessageBox::Yes) return; WalletModel::EncryptionStatus encStatus = walletModel->getEncryptionStatus(); if (encStatus == walletModel->Locked || encStatus == walletModel->UnlockedForAnonymizationOnly) { WalletModel::UnlockContext ctx(walletModel->requestUnlock(AskPassphraseDialog::Context::Unlock_Full)); if (!ctx.isValid()) return; // Unlock wallet was cancelled StartAlias(strAlias); return; } StartAlias(strAlias); } void MasternodeList::on_startAllButton_clicked() { // Display message box QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm all masternodes start"), tr("Are you sure you want to start ALL masternodes?"), QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel); if (retval != QMessageBox::Yes) return; WalletModel::EncryptionStatus encStatus = walletModel->getEncryptionStatus(); if (encStatus == walletModel->Locked || encStatus == walletModel->UnlockedForAnonymizationOnly) { WalletModel::UnlockContext ctx(walletModel->requestUnlock(AskPassphraseDialog::Context::Unlock_Full)); if (!ctx.isValid()) return; // Unlock wallet was cancelled StartAll(); return; } StartAll(); } void MasternodeList::on_startMissingButton_clicked() { if (!masternodeSync.IsMasternodeListSynced()) { QMessageBox::critical(this, tr("Command is not available right now"), tr("You can't use this command until masternode list is synced")); return; } // Display message box QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm missing masternodes start"), tr("Are you sure you want to start MISSING masternodes?"), QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel); if (retval != QMessageBox::Yes) return; WalletModel::EncryptionStatus encStatus = walletModel->getEncryptionStatus(); if (encStatus == walletModel->Locked || encStatus == walletModel->UnlockedForAnonymizationOnly) { WalletModel::UnlockContext ctx(walletModel->requestUnlock(AskPassphraseDialog::Context::Unlock_Full)); if (!ctx.isValid()) return; // Unlock wallet was cancelled StartAll("start-missing"); return; } StartAll("start-missing"); } void MasternodeList::on_tableWidgetMyMasternodes_itemSelectionChanged() { if (ui->tableWidgetMyMasternodes->selectedItems().count() > 0) { ui->startButton->setEnabled(true); } } void MasternodeList::on_UpdateButton_clicked() { updateMyNodeList(true); }