From b055613e89e2f87ee136e0cc57c9d42d021bfb3b Mon Sep 17 00:00:00 2001 From: Duke Leto Date: Sun, 7 Feb 2021 04:03:17 -0500 Subject: [PATCH] Peers tab --- silentdragon.pro | 2 + src/mainwindow.cpp | 5 ++ src/mainwindow.h | 5 +- src/mainwindow.ui | 31 +++++-- src/peerstablemodel.cpp | 185 ++++++++++++++++++++++++++++++++++++++++ src/peerstablemodel.h | 46 ++++++++++ src/rpc.cpp | 77 ++++++++++++++--- src/rpc.h | 32 +++++-- 8 files changed, 349 insertions(+), 34 deletions(-) create mode 100644 src/peerstablemodel.cpp create mode 100644 src/peerstablemodel.h diff --git a/silentdragon.pro b/silentdragon.pro index d1d8dd1..a45aa74 100644 --- a/silentdragon.pro +++ b/silentdragon.pro @@ -49,6 +49,7 @@ SOURCES += \ src/sendtab.cpp \ src/senttxstore.cpp \ src/txtablemodel.cpp \ + src/peerstablemodel.cpp \ src/qrcodelabel.cpp \ src/connection.cpp \ src/fillediconlabel.cpp \ @@ -73,6 +74,7 @@ HEADERS += \ src/3rdparty/qrcode/QrSegment.hpp \ src/settings.h \ src/txtablemodel.h \ + src/peerstablemodel.h \ src/senttxstore.h \ src/qrcodelabel.h \ src/connection.h \ diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 1e1b93d..d02f2c1 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -120,6 +120,7 @@ MainWindow::MainWindow(QWidget *parent) : setupMarketTab(); //setupChatTab(); setupHushTab(); + setupPeersTab(); rpc = new RPC(this); qDebug() << "Created RPC"; @@ -1126,6 +1127,10 @@ void MainWindow::setupBalancesTab() { }); } +void MainWindow::setupPeersTab() { + qDebug() << __FUNCTION__; +} + void MainWindow::setupHushTab() { ui->hushlogo->setBasePixmap(QPixmap(":/img/res/zcashdlogo.gif")); } diff --git a/src/mainwindow.h b/src/mainwindow.h index afda5c6..ecf9edd 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -68,7 +68,6 @@ public: QLabel* statusLabel; QLabel* statusIcon; QLabel* loadingLabel; - QWidget* zcashdtab; Logger* logger; @@ -78,6 +77,7 @@ private: void closeEvent(QCloseEvent* event); void setupSendTab(); + void setupPeersTab(); void setupTransactionsTab(); void setupReceiveTab(); void setupBalancesTab(); @@ -96,9 +96,6 @@ private: Tx createTxFromSendPage(); bool confirmTx(Tx tx); - void turnstileDoMigration(QString fromAddr = ""); - void turnstileProgress(); - void cancelButton(); void sendButton(); void inputComboTextChanged(int index); diff --git a/src/mainwindow.ui b/src/mainwindow.ui index 89f7acd..c7f1090 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -915,6 +915,7 @@ + Transactions @@ -932,6 +933,28 @@ + + + + + Peers + + + + + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + + + + + Market @@ -1612,14 +1635,6 @@ Check git.hush.is for &updates - - - Sapling &turnstile - - - Ctrl+A, Ctrl+T - - &Import private key diff --git a/src/peerstablemodel.cpp b/src/peerstablemodel.cpp new file mode 100644 index 0000000..b89e7d7 --- /dev/null +++ b/src/peerstablemodel.cpp @@ -0,0 +1,185 @@ +// Copyright 2019-2021 The Hush developers +// Released under the GPLv3 +#include "txtablemodel.h" +#include "settings.h" +#include "rpc.h" + +PeersTableModel::PeersTableModel(QObject *parent) + : QAbstractTableModel(parent) { + headers << QObject::tr("PeerID") << QObject::tr("Address") << QObject::tr("ASN") << QObject::tr("TLS Cipher") << QObject::tr("TLS Verfied") << QObject::tr("Version") << QObject::tr("Protocol Version") << QObject::tr("Ping Time") << QObject::tr("Banscore") << QObject::tr("Bytes received") << QObject::tr("Bytes sent"); +} + +PeersTableModel::~PeersTableModel() { + delete modeldata; +} + +void PeersTableModel::addData(const QList& data) { + peers = new QList(); + std::copy(data.begin(), data.end(), std::back_inserter(*peers)); + + updateAllData(); +} + +bool PeersTableModel::exportToCsv(QString fileName) const { + if (!modeldata) + return false; + + QFile file(fileName); + if (!file.open(QIODevice::ReadWrite | QIODevice::Truncate)) + return false; + + QTextStream out(&file); // we will serialize the data into the file + + // Write headers + for (int i = 0; i < headers.length(); i++) { + out << "\"" << headers[i] << "\","; + } + out << "\"Memo\""; + out << endl; + + // Write out each row + for (int row = 0; row < modeldata->length(); row++) { + for (int col = 0; col < headers.length(); col++) { + out << "\"" << data(index(row, col), Qt::DisplayRole).toString() << "\","; + } + } + + file.close(); + return true; +} + +void PeersTableModel::updateAllData() { + auto newmodeldata = new QList(); + + // Copy peer data so GUI can use it + if (peers != nullptr) std::copy( peers->begin(), peers->end(), std::back_inserter(*newmodeldata)); + + // Sort by connection time + std::sort(newmodeldata->begin(), newmodeldata->end(), [=] (auto a, auto b) { + return a.conntime > b.conntime; // reverse sort + }); + + // And then swap out the modeldata with the new one. + delete modeldata; + modeldata = newmodeldata; + + // do magic + dataChanged(index(0, 0), index(modeldata->size()-1, columnCount(index(0,0))-1)); + layoutChanged(); +} + +int PeersTableModel::rowCount(const QModelIndex&) const +{ + if (modeldata == nullptr) return 0; + return modeldata->size(); +} + +int PeersTableModel::columnCount(const QModelIndex&) const +{ + return headers.size(); +} + + + QVariant PeersTableModel::data(const QModelIndex &index, int role) const + { + // Align column 4 (amount) right + //if (role == Qt::TextAlignmentRole && index.column() == 3) return QVariant(Qt::AlignRight | Qt::AlignVCenter); + + if (role == Qt::ForegroundRole) { + // peers with banscore >=50 will likely be banned soon, color them red + if (modeldata->at(index.row()).banscore >= 50) { + QBrush b; + b.setColor(Qt::red); + return b; + } + + // Else, just return the default brush + QBrush b; + b.setColor(Qt::black); + return b; + } + + auto dat = modeldata->at(index.row()); + if (role == Qt::DisplayRole) { + switch (index.column()) { + case 0: return dat.peerid; + case 1: return dat.address; + case 2: return "AS" + QString::number(dat.asn); + case 3: return dat.tls_cipher; + case 4: return dat.tls_verified; + case 5: return dat.subver; + case 6: return dat.protocolversion; + case 7: return dat.pingtime; + case 8: return dat.banscore; + case 9: return dat.bytes_received; + case 10: return dat.bytes_sent; + } + } + + if (role == Qt::ToolTipRole) { + switch (index.column()) { + case 0: return ""; + } + } + + /* + if (role == Qt::DecorationRole && index.column() == 0) { + if (!dat.memo.isEmpty()) { + // If the memo is a Payment URI, then show a payment request icon + if (dat.memo.startsWith("hush:")) { + QIcon icon(":/icons/res/paymentreq.gif"); + return QVariant(icon.pixmap(16, 16)); + } else { + // Return the info pixmap to indicate memo + QIcon icon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxInformation); + return QVariant(icon.pixmap(16, 16)); + } + } else { + // Empty pixmap to make it align + QPixmap p(16, 16); + p.fill(Qt::white); + return QVariant(p); + } + } + */ + + return QVariant(); + } + + +QVariant PeersTableModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role == Qt::TextAlignmentRole && section == 3) return QVariant(Qt::AlignRight | Qt::AlignVCenter); + + if (role == Qt::FontRole) { + QFont f; + f.setBold(true); + return f; + } + + if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { + return headers.at(section); + } + + return QVariant(); +} + +QString PeersTableModel::getPeerId(int row) const { + return QString::number(modeldata->at(row).peerid); +} + +QString PeersTableModel::getAddress(int row) const { + return modeldata->at(row).address.trimmed(); +} + +qint64 PeersTableModel::getConntime(int row) const { + return modeldata->at(row).conntime; +} + +QString PeersTableModel::getType(int row) const { + return modeldata->at(row).type; +} + +QString PeersTableModel::getPingtime(int row) const { + return Settings::getDecimalString(modeldata->at(row).pingtime); +} diff --git a/src/peerstablemodel.h b/src/peerstablemodel.h new file mode 100644 index 0000000..862a003 --- /dev/null +++ b/src/peerstablemodel.h @@ -0,0 +1,46 @@ +// Copyright 2019-2021 The Hush developers +// Released under the GPLv3 +#ifndef PEERSTABLEMODEL_H +#define PEERSTABLEMODEL_H + +#include "precompiled.h" + +struct PeerItem; + +class PeersTableModel: public QAbstractTableModel +{ +public: + PeersTableModel(QObject* parent); + ~PeersTableModel(); + + QString getPeerId(int row) const; + QString getAddress(int row) const; + QString getType(int row) const; + qint64 getConntime(int row) const; + QString getASN(int row) const; + QString getSubver(int row) const; + QString getTLSCipher(int row) const; + bool getTLSVerified(int row) const; + QString getPingtime(int row) const; + unsigned int getBanscore(int row) const; + unsigned int getProtocolVersion(int row) const; + + bool exportToCsv(QString fileName) const; + + int rowCount(const QModelIndex &parent) const; + int columnCount(const QModelIndex &parent) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + + void addData (const QList& data); + +private: + void updateAllData(); + QList* peers = nullptr; + QList* modeldata = nullptr; + + QList headers; +}; + + +#endif // PEERSTABLEMODEL_H diff --git a/src/rpc.cpp b/src/rpc.cpp index 2ddd44f..915476a 100644 --- a/src/rpc.cpp +++ b/src/rpc.cpp @@ -14,7 +14,7 @@ RPC::RPC(MainWindow* main) { QTimer::singleShot(1, [=]() { cl->loadConnection(); }); this->main = main; - this->ui = main->ui; + this->ui = main->ui; // Setup balances table model balancesTableModel = new BalancesTableModel(main->ui->balancesTable); @@ -25,12 +25,17 @@ RPC::RPC(MainWindow* main) { main->ui->transactionsTable->setModel(transactionsTableModel); main->ui->transactionsTable->horizontalHeader()->setSectionResizeMode(3, QHeaderView::Stretch); + peersTableModel = new PeersTableModel(ui->peersTable); + main->ui->peersTable->setModel(peersTableModel); + // tls cipher is wide + main->ui->peersTable->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Stretch); + // Set up timer to refresh Price priceTimer = new QTimer(main); QObject::connect(priceTimer, &QTimer::timeout, [=]() { refreshPrice(); }); - priceTimer->start(Settings::priceRefreshSpeed); // Every hour + priceTimer->start(Settings::priceRefreshSpeed); // Set up a timer to refresh the UI every few seconds timer = new QTimer(main); @@ -46,7 +51,7 @@ RPC::RPC(MainWindow* main) { //qDebug() << "Watching tx status"; watchTxStatus(); }); - // Start at every 10s. When an operation is pending, this will change to every second + txTimer->start(Settings::updateSpeed); usedAddresses = new QMap(); @@ -58,6 +63,7 @@ RPC::~RPC() { delete transactionsTableModel; delete balancesTableModel; + delete peersTableModel; delete utxos; delete allBalances; @@ -70,10 +76,6 @@ RPC::~RPC() { void RPC::setEHushd(std::shared_ptr p) { ehushd = p; - - if (ehushd && ui->tabWidget->widget(4) == nullptr) { - ui->tabWidget->addTab(main->zcashdtab, "zcashd"); - } } // Called when a connection to hushd is available. @@ -85,7 +87,7 @@ void RPC::setConnection(Connection* c) { ui->statusBar->showMessage("Ready! Thank you for helping secure the Hush network by running a full node."); - // See if we need to remove the reindex/rescan flags from the zcash.conf file + // See if we need to remove the reindex/rescan flags from the conf file auto hushConfLocation = Settings::getInstance()->getHushdConfLocation(); Settings::removeFromHushConf(hushConfLocation, "rescan"); Settings::removeFromHushConf(hushConfLocation, "reindex"); @@ -239,6 +241,11 @@ void RPC::getBalance(const std::function& cb) { conn->doRPCWithDefaultErrorHandling(payload, cb); } +void RPC::getPeerInfo(const std::function& cb) { + QString method = "getpeerinfo"; + conn->doRPCWithDefaultErrorHandling(makePayload(method), cb); +} + void RPC::getTransactions(const std::function& cb) { QString method = "listtransactions"; conn->doRPCWithDefaultErrorHandling(makePayload(method), cb); @@ -573,11 +580,10 @@ void RPC::getInfoThenRefresh(bool force) { // TODO: store all future halvings int blocks_until_halving= 2020000 - curBlock; char halving_days[8]; - sprintf(halving_days, "%.2f", (double) (blocks_until_halving * 150) / (60*60*24) ); + int blocktime = 75; // seconds + sprintf(halving_days, "%.2f", (double) (blocks_until_halving * blocktime) / (60*60*24) ); QString ntzhash = reply["notarizedhash"].toString(); QString ntztxid = reply["notarizedtxid"].toString(); - // Fuck The KYC Traitor named jl777 - //QString kmdver = reply["KMDversion"].toString(); Settings::getInstance()->setHushdVersion(version); @@ -586,7 +592,6 @@ void RPC::getInfoThenRefresh(bool force) { ui->notarizedtxidvalue->setText( ntztxid ); ui->lagvalue->setText( QString::number(lag) ); ui->version->setText( QString::number(version) ); - //ui->kmdversion->setText( kmdver ); ui->protocolversion->setText( QString::number(protocolversion) ); ui->p2pport->setText( QString::number(p2pport) ); ui->rpcport->setText( QString::number(rpcport) ); @@ -597,6 +602,7 @@ void RPC::getInfoThenRefresh(bool force) { lastBlock = curBlock; refreshBalances(); + refreshPeers(); refreshAddresses(); // This calls refreshZSentTransactions() and refreshReceivedZTrans() refreshTransactions(); } @@ -621,8 +627,8 @@ void RPC::getInfoThenRefresh(bool force) { conn->doRPCIgnoreError(makePayload(method), [=](const QJsonValue& reply) { qint64 solrate = reply.toInt(); - // TODO: megasol - ui->solrate->setText(QString::number(solrate) % " Sol/s"); + //TODO: format decimal + ui->solrate->setText(QString::number(solrate / 1000000) % " MegaSol/s"); }); // Get network info @@ -850,6 +856,49 @@ void RPC::refreshBalances() { }); } +void RPC::refreshPeers() { + qDebug() << __func__; + if (conn == nullptr) + return noConnection(); + + getPeerInfo([=] (QJsonValue reply) { + QList peerdata; + + for (const auto& it : reply.toArray()) { + auto addr = it.toObject()["addr"].toString(); + auto asn = (qint64)it.toObject()["mapped_as"].toInt(); + auto recv = (qint64)it.toObject()["bytesrecv"].toInt(); + auto sent = (qint64)it.toObject()["bytessent"].toInt(); + + PeerItem peer { + it.toObject()["id"].toInt(), // peerid + "", // type + (qint64)it.toObject()["conntime"].toInt(), + addr, + asn, + it.toObject()["tls_cipher"].toString(), + // don't convert the JS string "false" to a bool, which is true, lulz + it.toObject()["tls_verified"].toString() == 'true' ? true : false, + (qint64)(it.toObject()["banscore"].toInt()), + (qint64)(it.toObject()["version"].toInt()), // protocolversion + it.toObject()["subver"].toString(), + recv, + sent, + it.toObject()["pingtime"].toDouble(), + }; + + qDebug() << "Adding peer with address=" << addr << " and AS=" << asn + << " bytesrecv=" << recv << " bytessent=" << sent; + peerdata.push_back(peer); + } + + //qDebug() << peerdata; + + // Update model data, which updates the table view + peersTableModel->addData(peerdata); + }); +} + void RPC::refreshTransactions() { if (conn == nullptr) return noConnection(); diff --git a/src/rpc.h b/src/rpc.h index 06c1ae8..88dc4aa 100644 --- a/src/rpc.h +++ b/src/rpc.h @@ -4,18 +4,16 @@ #define RPCCLIENT_H #include "precompiled.h" - #include "balancestablemodel.h" #include "txtablemodel.h" +#include "peerstablemodel.h" #include "ui_mainwindow.h" #include "mainwindow.h" #include "connection.h" -class Turnstile; - struct TransactionItem { QString type; - qint64 datetime; + qint64 datetime; QString address; QString txid; double amount; @@ -24,6 +22,23 @@ struct TransactionItem { QString memo; }; +struct PeerItem { + qint64 peerid; + QString type; + qint64 conntime; + QString address; + qint64 asn; + QString tls_cipher; + bool tls_verified; + qint64 banscore; + qint64 protocolversion; + QString subver; + qint64 bytes_received; + qint64 bytes_sent; + double pingtime; +}; + + struct WatchedTx { QString opid; Tx tx; @@ -44,6 +59,7 @@ public: void refresh(bool force = false); void refreshAddresses(); + void refreshPeers(); void checkForUpdate(bool silent = true); void refreshPrice(); @@ -62,6 +78,7 @@ public: void addNewTxToWatch(const QString& newOpid, WatchedTx wtx); const TxTableModel* getTransactionsModel() { return transactionsTableModel; } + const PeersTableModel* getPeersModel() { return peersTableModel; } const QList* getAllZAddresses() { return zaddresses; } const QList* getAllTAddresses() { return taddresses; } const QList* getUTXOs() { return utxos; } @@ -87,7 +104,6 @@ public: void getAllPrivKeys(const std::function>)>); - Turnstile* getTurnstile() { return turnstile; } Connection* getConnection() { return conn; } private: @@ -109,12 +125,12 @@ private: void getTransparentUnspent (const std::function& cb); void getZUnspent (const std::function& cb); void getTransactions (const std::function& cb); + void getPeerInfo (const std::function& cb); void getZAddresses (const std::function& cb); void getTAddresses (const std::function& cb); Connection* conn = nullptr; - std::shared_ptr ehushd = nullptr; - + std::shared_ptr ehushd = nullptr; QList* utxos = nullptr; QMap* allBalances = nullptr; QMap* usedAddresses = nullptr; @@ -124,6 +140,7 @@ private: QMap watchingOps; TxTableModel* transactionsTableModel = nullptr; + PeersTableModel* peersTableModel = nullptr; BalancesTableModel* balancesTableModel = nullptr; QTimer* timer; @@ -132,7 +149,6 @@ private: Ui::MainWindow* ui; MainWindow* main; - Turnstile* turnstile; // Current balance in the UI. If this number updates, then refresh the UI QString currentBalance;