mirror of
https://github.com/dashpay/dash.git
synced 2024-12-29 13:59:06 +01:00
296cfd2efa
* Fix compilation with qt < 5.2 * remove code duplication
428 lines
16 KiB
C++
428 lines
16 KiB
C++
#include "masternodelist.h"
|
|
#include "ui_masternodelist.h"
|
|
|
|
#include "activemasternode.h"
|
|
#include "clientmodel.h"
|
|
#include "init.h"
|
|
#include "guiutil.h"
|
|
#include "masternode-sync.h"
|
|
#include "masternodeconfig.h"
|
|
#include "masternodeman.h"
|
|
#include "sync.h"
|
|
#include "wallet/wallet.h"
|
|
#include "walletmodel.h"
|
|
|
|
#include <QTimer>
|
|
#include <QMessageBox>
|
|
|
|
int GetOffsetFromUtc()
|
|
{
|
|
#if QT_VERSION < 0x050200
|
|
const QDateTime dateTime1 = QDateTime::currentDateTime();
|
|
const QDateTime dateTime2 = QDateTime(dateTime1.date(), dateTime1.time(), Qt::UTC);
|
|
return dateTime1.secsTo(dateTime2);
|
|
#else
|
|
return QDateTime::currentDateTime().offsetFromUtc();
|
|
#endif
|
|
}
|
|
|
|
MasternodeList::MasternodeList(const PlatformStyle *platformStyle, 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->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->tableWidgetMasternodes->setColumnWidth(0, columnAddressWidth);
|
|
ui->tableWidgetMasternodes->setColumnWidth(1, columnProtocolWidth);
|
|
ui->tableWidgetMasternodes->setColumnWidth(2, columnStatusWidth);
|
|
ui->tableWidgetMasternodes->setColumnWidth(3, columnActiveWidth);
|
|
ui->tableWidgetMasternodes->setColumnWidth(4, 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(updateNodeList()));
|
|
connect(timer, SIGNAL(timeout()), this, SLOT(updateMyNodeList()));
|
|
timer->start(1000);
|
|
|
|
fFilterUpdated = false;
|
|
nTimeFilterUpdated = GetTime();
|
|
updateNodeList();
|
|
}
|
|
|
|
MasternodeList::~MasternodeList()
|
|
{
|
|
delete ui;
|
|
}
|
|
|
|
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()));
|
|
}
|
|
}
|
|
|
|
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 += "<center>Alias: " + strAlias;
|
|
|
|
BOOST_FOREACH(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 += "<br>Successfully started masternode.";
|
|
mnodeman.UpdateMasternodeList(mnb, *g_connman);
|
|
mnb.Relay(*g_connman);
|
|
mnodeman.NotifyMasternodeUpdates(*g_connman);
|
|
} else {
|
|
strStatusHtml += "<br>Failed to start masternode.<br>Error: " + strError;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
strStatusHtml += "</center>";
|
|
|
|
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;
|
|
|
|
BOOST_FOREACH(CMasternodeConfig::CMasternodeEntry mne, masternodeConfig.getEntries()) {
|
|
std::string strError;
|
|
CMasternodeBroadcast mnb;
|
|
|
|
int32_t nOutputIndex = 0;
|
|
if(!ParseInt32(mne.getOutputIndex(), &nOutputIndex)) {
|
|
continue;
|
|
}
|
|
|
|
COutPoint outpoint = COutPoint(uint256S(mne.getTxHash()), nOutputIndex);
|
|
|
|
if(strCommand == "start-missing" && mnodeman.Has(outpoint)) continue;
|
|
|
|
bool fSuccess = CMasternodeBroadcast::Create(mne.getIp(), mne.getPrivKey(), mne.getTxHash(), mne.getOutputIndex(), strError, mnb);
|
|
|
|
if(fSuccess) {
|
|
nCountSuccessful++;
|
|
mnodeman.UpdateMasternodeList(mnb, *g_connman);
|
|
mnb.Relay(*g_connman);
|
|
mnodeman.NotifyMasternodeUpdates(*g_connman);
|
|
} 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, const COutPoint& outpoint)
|
|
{
|
|
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);
|
|
}
|
|
|
|
masternode_info_t infoMn;
|
|
bool fFound = mnodeman.GetMasternodeInfo(outpoint, infoMn);
|
|
|
|
QTableWidgetItem *aliasItem = new QTableWidgetItem(strAlias);
|
|
QTableWidgetItem *addrItem = new QTableWidgetItem(fFound ? QString::fromStdString(infoMn.addr.ToString()) : strAddr);
|
|
QTableWidgetItem *protocolItem = new QTableWidgetItem(QString::number(fFound ? infoMn.nProtocolVersion : -1));
|
|
QTableWidgetItem *statusItem = new QTableWidgetItem(QString::fromStdString(fFound ? CMasternode::StateToString(infoMn.nActiveState) : "MISSING"));
|
|
QTableWidgetItem *activeSecondsItem = new QTableWidgetItem(QString::fromStdString(DurationToDHMS(fFound ? (infoMn.nTimeLastPing - infoMn.sigTime) : 0)));
|
|
QTableWidgetItem *lastSeenItem = new QTableWidgetItem(QString::fromStdString(DateTimeStrFormat("%Y-%m-%d %H:%M",
|
|
fFound ? infoMn.nTimeLastPing + GetOffsetFromUtc() : 0)));
|
|
QTableWidgetItem *pubkeyItem = new QTableWidgetItem(QString::fromStdString(fFound ? CBitcoinAddress(infoMn.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)
|
|
{
|
|
TRY_LOCK(cs_mymnlist, fLockAcquired);
|
|
if(!fLockAcquired) {
|
|
return;
|
|
}
|
|
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->tableWidgetMasternodes->setSortingEnabled(false);
|
|
BOOST_FOREACH(CMasternodeConfig::CMasternodeEntry mne, masternodeConfig.getEntries()) {
|
|
int32_t nOutputIndex = 0;
|
|
if(!ParseInt32(mne.getOutputIndex(), &nOutputIndex)) {
|
|
continue;
|
|
}
|
|
|
|
updateMyMasternodeInfo(QString::fromStdString(mne.getAlias()), QString::fromStdString(mne.getIp()), COutPoint(uint256S(mne.getTxHash()), nOutputIndex));
|
|
}
|
|
ui->tableWidgetMasternodes->setSortingEnabled(true);
|
|
|
|
// reset "timer"
|
|
ui->secondsLabel->setText("0");
|
|
}
|
|
|
|
void MasternodeList::updateNodeList()
|
|
{
|
|
TRY_LOCK(cs_mnlist, 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 = fFilterUpdated
|
|
? nTimeFilterUpdated - GetTime() + MASTERNODELIST_FILTER_COOLDOWN_SECONDS
|
|
: nTimeListUpdated - GetTime() + MASTERNODELIST_UPDATE_SECONDS;
|
|
|
|
if(fFilterUpdated) ui->countLabel->setText(QString::fromStdString(strprintf("Please wait... %d", nSecondsToWait)));
|
|
if(nSecondsToWait > 0) return;
|
|
|
|
nTimeListUpdated = GetTime();
|
|
fFilterUpdated = false;
|
|
|
|
QString strToFilter;
|
|
ui->countLabel->setText("Updating...");
|
|
ui->tableWidgetMasternodes->setSortingEnabled(false);
|
|
ui->tableWidgetMasternodes->clearContents();
|
|
ui->tableWidgetMasternodes->setRowCount(0);
|
|
std::map<COutPoint, CMasternode> mapMasternodes = mnodeman.GetFullMasternodeMap();
|
|
int offsetFromUtc = GetOffsetFromUtc();
|
|
|
|
for(auto& mnpair : mapMasternodes)
|
|
{
|
|
CMasternode mn = mnpair.second;
|
|
// populate list
|
|
// Address, Protocol, Status, Active Seconds, Last Seen, Pub Key
|
|
QTableWidgetItem *addressItem = new QTableWidgetItem(QString::fromStdString(mn.addr.ToString()));
|
|
QTableWidgetItem *protocolItem = new QTableWidgetItem(QString::number(mn.nProtocolVersion));
|
|
QTableWidgetItem *statusItem = new QTableWidgetItem(QString::fromStdString(mn.GetStatus()));
|
|
QTableWidgetItem *activeSecondsItem = new QTableWidgetItem(QString::fromStdString(DurationToDHMS(mn.lastPing.sigTime - mn.sigTime)));
|
|
QTableWidgetItem *lastSeenItem = new QTableWidgetItem(QString::fromStdString(DateTimeStrFormat("%Y-%m-%d %H:%M", mn.lastPing.sigTime + offsetFromUtc)));
|
|
QTableWidgetItem *pubkeyItem = new QTableWidgetItem(QString::fromStdString(CBitcoinAddress(mn.pubKeyCollateralAddress.GetID()).ToString()));
|
|
|
|
if (strCurrentFilter != "")
|
|
{
|
|
strToFilter = addressItem->text() + " " +
|
|
protocolItem->text() + " " +
|
|
statusItem->text() + " " +
|
|
activeSecondsItem->text() + " " +
|
|
lastSeenItem->text() + " " +
|
|
pubkeyItem->text();
|
|
if (!strToFilter.contains(strCurrentFilter)) continue;
|
|
}
|
|
|
|
ui->tableWidgetMasternodes->insertRow(0);
|
|
ui->tableWidgetMasternodes->setItem(0, 0, addressItem);
|
|
ui->tableWidgetMasternodes->setItem(0, 1, protocolItem);
|
|
ui->tableWidgetMasternodes->setItem(0, 2, statusItem);
|
|
ui->tableWidgetMasternodes->setItem(0, 3, activeSecondsItem);
|
|
ui->tableWidgetMasternodes->setItem(0, 4, lastSeenItem);
|
|
ui->tableWidgetMasternodes->setItem(0, 5, pubkeyItem);
|
|
}
|
|
|
|
ui->countLabel->setText(QString::number(ui->tableWidgetMasternodes->rowCount()));
|
|
ui->tableWidgetMasternodes->setSortingEnabled(true);
|
|
}
|
|
|
|
void MasternodeList::on_filterLineEdit_textChanged(const QString &strFilterIn)
|
|
{
|
|
strCurrentFilter = strFilterIn;
|
|
nTimeFilterUpdated = GetTime();
|
|
fFilterUpdated = true;
|
|
ui->countLabel->setText(QString::fromStdString(strprintf("Please wait... %d", MASTERNODELIST_FILTER_COOLDOWN_SECONDS)));
|
|
}
|
|
|
|
void MasternodeList::on_startButton_clicked()
|
|
{
|
|
std::string strAlias;
|
|
{
|
|
LOCK(cs_mymnlist);
|
|
// 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();
|
|
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->UnlockedForMixingOnly) {
|
|
WalletModel::UnlockContext ctx(walletModel->requestUnlock());
|
|
|
|
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->UnlockedForMixingOnly) {
|
|
WalletModel::UnlockContext ctx(walletModel->requestUnlock());
|
|
|
|
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->UnlockedForMixingOnly) {
|
|
WalletModel::UnlockContext ctx(walletModel->requestUnlock());
|
|
|
|
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);
|
|
}
|