neobytes/src/qt/masternodelist.cpp

428 lines
16 KiB
C++
Raw Normal View History

2016-05-11 13:09:31 +02:00
#include "masternodelist.h"
#include "ui_masternodelist.h"
#include "activemasternode.h"
#include "clientmodel.h"
#include "init.h"
#include "guiutil.h"
2016-05-11 13:09:31 +02:00
#include "masternode-sync.h"
#include "masternodeconfig.h"
#include "masternodeman.h"
#include "sync.h"
2016-05-11 13:09:31 +02:00
#include "wallet/wallet.h"
#include "walletmodel.h"
2016-05-11 13:09:31 +02:00
#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
}
2016-05-11 13:09:31 +02:00
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);
2016-05-11 13:09:31 +02:00
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()));
2016-05-11 13:09:31 +02:00
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();
2016-05-11 13:09:31 +02:00
updateNodeList();
}
MasternodeList::~MasternodeList()
{
delete ui;
}
void MasternodeList::setClientModel(ClientModel *model)
{
this->clientModel = model;
if(model) {
2016-05-11 13:09:31 +02:00
// try to update list when masternode count changes
2016-05-16 21:27:43 +02:00
connect(clientModel, SIGNAL(strMasternodesChanged(QString)), this, SLOT(updateNodeList()));
2016-05-11 13:09:31 +02:00
}
}
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());
}
2016-05-11 13:09:31 +02:00
void MasternodeList::StartAlias(std::string strAlias)
{
std::string strStatusHtml;
strStatusHtml += "<center>Alias: " + strAlias;
2016-05-11 13:09:31 +02:00
BOOST_FOREACH(CMasternodeConfig::CMasternodeEntry mne, masternodeConfig.getEntries()) {
if(mne.getAlias() == strAlias) {
std::string strError;
2016-05-11 13:09:31 +02:00
CMasternodeBroadcast mnb;
bool fSuccess = CMasternodeBroadcast::Create(mne.getIp(), mne.getPrivKey(), mne.getTxHash(), mne.getOutputIndex(), strError, mnb);
2016-05-11 13:09:31 +02:00
if(fSuccess) {
strStatusHtml += "<br>Successfully started masternode.";
Eliminate remaining uses of g_connman in Dash-specific code. (#1635) This monstrous change eliminates all remaining uses of g_connman global variable in Dash-specific code. Unlike previous changes eliminating g_connman use that were isolated to particular modules, this one covers multiple modules simultaneously because they are so interdependent that change in one module was quickly spreading to others. This is mostly invariant change that was done by * changing all functions using g_connman to use connman argument, * changing all functions calling these functions to use connman argument, * repeating previous step until there's nothing to change. After multiple iterations, this process converged to final result, producing code that is mostly equivalent to original one, but passing CConnman instance through arguments instead of global variable. The only exception to equivalence of resulting code is that I had to create overload of CMasternodeMan::CheckAndRemove() method without arguments that does nothing just for use in CFlatDB<CMasternodeMan>::Dump() and CFlatDB<CMasternodeMan>::Load() methods. Normal CMasternodeMan::CheckAndRemove() overload now has argument of CConnman& type and is used everywhere else. The normal overload has this code in the beginning: if(!masternodeSync.IsMasternodeListSynced()) return; Masternode list is not synced yet when we load "mncache.dat" file, and we save "mncache.dat" file on shutdown, so I presume that it's OK to use overload that does nothing in both cases. Signed-off-by: Oleg Girko <ol@infoserver.lv>
2017-09-19 16:51:38 +02:00
mnodeman.UpdateMasternodeList(mnb, *g_connman);
mnb.Relay(*g_connman);
mnodeman.NotifyMasternodeUpdates(*g_connman);
2016-05-11 13:09:31 +02:00
} else {
strStatusHtml += "<br>Failed to start masternode.<br>Error: " + strError;
2016-05-11 13:09:31 +02:00
}
break;
}
}
strStatusHtml += "</center>";
2016-05-11 13:09:31 +02:00
QMessageBox msg;
msg.setText(QString::fromStdString(strStatusHtml));
2016-05-11 13:09:31 +02:00
msg.exec();
updateMyNodeList(true);
2016-05-11 13:09:31 +02:00
}
void MasternodeList::StartAll(std::string strCommand)
{
int nCountSuccessful = 0;
int nCountFailed = 0;
std::string strFailedHtml;
2016-05-11 13:09:31 +02:00
BOOST_FOREACH(CMasternodeConfig::CMasternodeEntry mne, masternodeConfig.getEntries()) {
std::string strError;
2016-05-11 13:09:31 +02:00
CMasternodeBroadcast mnb;
int32_t nOutputIndex = 0;
if(!ParseInt32(mne.getOutputIndex(), &nOutputIndex)) {
continue;
}
COutPoint outpoint = COutPoint(uint256S(mne.getTxHash()), nOutputIndex);
2016-05-11 13:09:31 +02:00
if(strCommand == "start-missing" && mnodeman.Has(outpoint)) continue;
2016-05-11 13:09:31 +02:00
bool fSuccess = CMasternodeBroadcast::Create(mne.getIp(), mne.getPrivKey(), mne.getTxHash(), mne.getOutputIndex(), strError, mnb);
2016-05-11 13:09:31 +02:00
if(fSuccess) {
nCountSuccessful++;
Eliminate remaining uses of g_connman in Dash-specific code. (#1635) This monstrous change eliminates all remaining uses of g_connman global variable in Dash-specific code. Unlike previous changes eliminating g_connman use that were isolated to particular modules, this one covers multiple modules simultaneously because they are so interdependent that change in one module was quickly spreading to others. This is mostly invariant change that was done by * changing all functions using g_connman to use connman argument, * changing all functions calling these functions to use connman argument, * repeating previous step until there's nothing to change. After multiple iterations, this process converged to final result, producing code that is mostly equivalent to original one, but passing CConnman instance through arguments instead of global variable. The only exception to equivalence of resulting code is that I had to create overload of CMasternodeMan::CheckAndRemove() method without arguments that does nothing just for use in CFlatDB<CMasternodeMan>::Dump() and CFlatDB<CMasternodeMan>::Load() methods. Normal CMasternodeMan::CheckAndRemove() overload now has argument of CConnman& type and is used everywhere else. The normal overload has this code in the beginning: if(!masternodeSync.IsMasternodeListSynced()) return; Masternode list is not synced yet when we load "mncache.dat" file, and we save "mncache.dat" file on shutdown, so I presume that it's OK to use overload that does nothing in both cases. Signed-off-by: Oleg Girko <ol@infoserver.lv>
2017-09-19 16:51:38 +02:00
mnodeman.UpdateMasternodeList(mnb, *g_connman);
mnb.Relay(*g_connman);
mnodeman.NotifyMasternodeUpdates(*g_connman);
2016-05-11 13:09:31 +02:00
} else {
nCountFailed++;
strFailedHtml += "\nFailed to start " + mne.getAlias() + ". Error: " + strError;
2016-05-11 13:09:31 +02:00
}
}
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;
}
2016-05-11 13:09:31 +02:00
QMessageBox msg;
msg.setText(QString::fromStdString(returnObj));
msg.exec();
updateMyNodeList(true);
2016-05-11 13:09:31 +02:00
}
void MasternodeList::updateMyMasternodeInfo(QString strAlias, QString strAddr, const COutPoint& outpoint)
2016-05-11 13:09:31 +02:00
{
bool fOldRowFound = false;
int nNewRow = 0;
2016-05-11 13:09:31 +02:00
for(int i = 0; i < ui->tableWidgetMyMasternodes->rowCount(); i++) {
if(ui->tableWidgetMyMasternodes->item(i, 0)->text() == strAlias) {
fOldRowFound = true;
nNewRow = i;
2016-05-11 13:09:31 +02:00
break;
}
}
if(nNewRow == 0 && !fOldRowFound) {
nNewRow = ui->tableWidgetMyMasternodes->rowCount();
ui->tableWidgetMyMasternodes->insertRow(nNewRow);
2016-05-11 13:09:31 +02:00
}
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() : ""));
2016-05-11 13:09:31 +02:00
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);
2016-05-11 13:09:31 +02:00
}
void MasternodeList::updateMyNodeList(bool fForce)
{
TRY_LOCK(cs_mymnlist, fLockAcquired);
if(!fLockAcquired) {
return;
}
static int64_t nTimeMyListUpdated = 0;
2016-05-11 13:09:31 +02:00
// 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));
2016-05-11 13:09:31 +02:00
if(nSecondsTillUpdate > 0 && !fForce) return;
nTimeMyListUpdated = GetTime();
2016-05-11 13:09:31 +02:00
ui->tableWidgetMasternodes->setSortingEnabled(false);
2016-05-11 13:09:31 +02:00
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));
2016-05-11 13:09:31 +02:00
}
ui->tableWidgetMasternodes->setSortingEnabled(true);
2016-05-11 13:09:31 +02:00
// reset "timer"
ui->secondsLabel->setText("0");
}
void MasternodeList::updateNodeList()
{
TRY_LOCK(cs_mnlist, fLockAcquired);
if(!fLockAcquired) {
return;
}
static int64_t nTimeListUpdated = GetTime();
2016-05-11 13:09:31 +02:00
// 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;
2016-05-11 13:09:31 +02:00
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();
2016-05-11 13:09:31 +02:00
for(auto& mnpair : mapMasternodes)
2016-05-11 13:09:31 +02:00
{
CMasternode mn = mnpair.second;
2016-05-11 13:09:31 +02:00
// 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()));
2016-05-11 13:09:31 +02:00
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()));
2016-05-11 13:09:31 +02:00
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);
2016-05-11 13:09:31 +02:00
}
ui->countLabel->setText(QString::number(ui->tableWidgetMasternodes->rowCount()));
ui->tableWidgetMasternodes->setSortingEnabled(true);
2016-05-11 13:09:31 +02:00
}
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)));
2016-05-11 13:09:31 +02:00
}
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;
2016-05-11 13:09:31 +02:00
QModelIndex index = selected.at(0);
int nSelectedRow = index.row();
strAlias = ui->tableWidgetMyMasternodes->item(nSelectedRow, 0)->text().toStdString();
}
2016-05-11 13:09:31 +02:00
// 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;
2016-05-11 13:09:31 +02:00
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
2016-05-11 13:09:31 +02:00
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;
2016-05-11 13:09:31 +02:00
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
2016-05-11 13:09:31 +02:00
StartAll();
return;
}
StartAll();
}
void MasternodeList::on_startMissingButton_clicked()
{
if(!masternodeSync.IsMasternodeListSynced()) {
2016-05-11 13:09:31 +02:00
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;
2016-05-11 13:09:31 +02:00
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
2016-05-11 13:09:31 +02:00
StartAll("start-missing");
return;
}
StartAll("start-missing");
}
void MasternodeList::on_tableWidgetMyMasternodes_itemSelectionChanged()
2016-05-11 13:09:31 +02:00
{
if(ui->tableWidgetMyMasternodes->selectedItems().count() > 0) {
2016-05-11 13:09:31 +02:00
ui->startButton->setEnabled(true);
}
}
void MasternodeList::on_UpdateButton_clicked()
{
updateMyNodeList(true);
}