From 7d0e9d137d900376824a0b9025a76103c853b701 Mon Sep 17 00:00:00 2001
From: devhyper <57877914+devhyper@users.noreply.github.com>
Date: Mon, 2 Oct 2023 20:50:36 -0700
Subject: [PATCH] p2pool: Detailed statistics
---
pages/Advanced.qml | 5 +
pages/Mining.qml | 31 ++++++
pages/MiningStats.qml | 203 +++++++++++++++++++++++++++++++++++
qml.qrc | 1 +
src/p2pool/P2PoolManager.cpp | 18 ++++
src/p2pool/P2PoolManager.h | 2 +
6 files changed, 260 insertions(+)
create mode 100644 pages/MiningStats.qml
diff --git a/pages/Advanced.qml b/pages/Advanced.qml
index d9c868aec4..ae815f4aa5 100644
--- a/pages/Advanced.qml
+++ b/pages/Advanced.qml
@@ -78,6 +78,7 @@ ColumnLayout {
property Item currentView
property Item previousView
property Mining miningView: Mining { }
+ property MiningStats miningStatsView: MiningStats { }
property TxKey prooveView: TxKey { }
property SharedRingDB sharedRingDBView: SharedRingDB { }
property Sign signView: Sign { }
@@ -106,6 +107,10 @@ ColumnLayout {
name: "Mining"
PropertyChanges { target: stateView; currentView: stateView.miningView }
PropertyChanges { target: root; panelHeight: stateView.miningView.miningHeight + 140 }
+ }, State {
+ name: "MiningStats"
+ PropertyChanges { target: stateView; currentView: stateView.miningStatsView }
+ PropertyChanges { target: root; panelHeight: stateView.miningStatsView.miningStatsHeight + 140 }
}, State {
name: "Prove"
PropertyChanges { target: stateView; currentView: stateView.prooveView }
diff --git a/pages/Mining.qml b/pages/Mining.qml
index e0f220aad8..17c02acd65 100644
--- a/pages/Mining.qml
+++ b/pages/Mining.qml
@@ -379,6 +379,37 @@ Rectangle {
}
}
+ ColumnLayout {
+ Layout.fillWidth: true
+ Layout.alignment : Qt.AlignTop | Qt.AlignLeft
+
+ MoneroComponents.Label {
+ id: statisticsLabel
+ visible: persistentSettings.allow_p2pool_mining
+ color: MoneroComponents.Style.defaultFontColor
+ text: qsTr("Statistics") + translationManager.emptyString
+ fontSize: 16
+ }
+ }
+
+ ColumnLayout {
+ Layout.fillWidth: true
+ spacing: 16
+
+ MoneroComponents.StandardButton {
+ id: miningStatsButton
+ small: true
+ primary: false
+ text: qsTr("Open mining statistics") + translationManager.emptyString
+ enabled: appWindow.isMining
+ visible: persistentSettings.allow_p2pool_mining
+ onClicked: {
+ p2poolManager.getStats();
+ stateView.state = "MiningStats";
+ }
+ }
+ }
+
ListModel {
id: chainModel
diff --git a/pages/MiningStats.qml b/pages/MiningStats.qml
new file mode 100644
index 0000000000..7d13614f54
--- /dev/null
+++ b/pages/MiningStats.qml
@@ -0,0 +1,203 @@
+// Copyright (c) 2014-2019, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import QtQml.Models 2.2
+import QtQuick 2.9
+import QtQuick.Layouts 1.1
+import "../components" as MoneroComponents
+import moneroComponents.P2PoolManager 1.0
+
+Rectangle {
+ id: root
+ color: "transparent"
+ property alias miningStatsHeight: miningStatsContentColumn.height
+ property int entryHeight: 24
+ property int entryPixelSize: 14
+ property bool showFullStats: false
+
+ Timer {
+ id: timer
+ interval: 5000
+ running: stateView.state === "MiningStats";
+ repeat: true
+ onTriggered: update()
+ }
+
+ function update()
+ {
+ if (persistentSettings.allow_p2pool_mining) {
+ p2poolManager.getStats();
+ }
+ }
+
+ function recursivelyFindPairs(statsMap, depth)
+ {
+ for (var key in statsMap)
+ {
+ var value = statsMap[key];
+ if (typeof value === 'object') {
+ miningStatsModel.append({"key": key + ":", "value": "", "depth": depth});
+ recursivelyFindPairs(value, depth + 1);
+ }
+ else {
+ miningStatsModel.append({"key": key + ":", "value": value.toString(), "depth": depth});
+ }
+ }
+ }
+
+ function updateSimpleMiningStats(statsMap)
+ {
+ var pool_statistics = statsMap["pool_statistics"];
+
+ if (pool_statistics != null)
+ {
+ var statsData = [
+ {"key": "Your hashrate:", "value": statsMap["current_hashrate"] + " H/s"},
+
+ {"key": "Main chain height:", "value": statsMap["height"]},
+ {"key": "Side chain height:", "value": pool_statistics["sidechainHeight"]},
+ {"key": "PPLNS window:", "value": pool_statistics["pplnsWindowSize"] + " blocks"},
+ {"key": "Your shares:", "value": statsMap["shares_found"] + " blocks"},
+ {"key": "Block reward share:", "value": statsMap["block_reward_share_percent"] + "%"},
+
+ {"key": "Connections: ", "value": statsMap["connections"] + " (" + statsMap["incoming_connections"] + " incoming)"},
+ {"key": "Peer list size: ", "value": statsMap["peer_list_size"]},
+ {"key": "Uptime: ", "value": statsMap["uptime"] + "s"}
+ ];
+
+ for (var element of statsData)
+ {
+ if (element.value != null)
+ {
+ simpleMiningStatsModel.append({"key": element["key"], "value": element["value"].toString(), "depth": 0});
+ }
+ }
+ }
+ }
+
+ function updateMiningStats(statsMap)
+ {
+ simpleMiningStatsModel.clear();
+ updateSimpleMiningStats(statsMap);
+
+ miningStatsModel.clear();
+ recursivelyFindPairs(statsMap, 0);
+ }
+
+ ListModel { id: simpleMiningStatsModel }
+ ListModel { id: miningStatsModel }
+
+ Column {
+ id: miningStatsContentColumn
+ spacing: entryHeight
+
+ MoneroComponents.StandardButton {
+ id: backButton
+ text: qsTr("Back") + translationManager.emptyString;
+ width: 100
+ primary: false
+ onClicked: {
+ stateView.state = "Mining";
+ }
+ }
+
+ ListView {
+ id: simpleMiningStatsView
+ width: root.width
+ height: count * entryHeight + entryHeight
+ model: simpleMiningStatsModel
+ interactive: false
+
+ delegate: Rectangle {
+ id: miningStatsDelegate
+ color: "transparent"
+ height: entryHeight
+ Layout.fillWidth: true
+
+ RowLayout {
+ MoneroComponents.TextBlock {
+ Layout.fillWidth: true
+ Layout.leftMargin: depth * entryPixelSize
+ font.pixelSize: entryPixelSize
+ text: key + translationManager.emptyString
+ }
+
+ MoneroComponents.TextBlock {
+ Layout.fillWidth: true
+ color: MoneroComponents.Style.dimmedFontColor
+ font.pixelSize: entryPixelSize
+ text: value + translationManager.emptyString
+ }
+ }
+ }
+ }
+
+ MoneroComponents.CheckBox2 {
+ id: showFullStatsCheckbox
+ checked: showFullStats
+ onClicked: showFullStats = !showFullStats
+ text: qsTr("Show full statistics") + translationManager.emptyString
+ }
+
+ ListView {
+ visible: showFullStats
+ id: miningStatsView
+ width: root.width
+ height: count * entryHeight + entryHeight
+ model: miningStatsModel
+ interactive: false
+
+ delegate: Rectangle {
+ id: miningStatsDelegate
+ color: "transparent"
+ height: entryHeight
+ Layout.fillWidth: true
+
+ RowLayout {
+ MoneroComponents.TextBlock {
+ Layout.fillWidth: true
+ Layout.leftMargin: depth * entryPixelSize
+ font.pixelSize: entryPixelSize
+ text: key + translationManager.emptyString
+ }
+
+ MoneroComponents.TextBlock {
+ Layout.fillWidth: true
+ color: MoneroComponents.Style.dimmedFontColor
+ font.pixelSize: entryPixelSize
+ text: value + translationManager.emptyString
+ }
+ }
+ }
+ }
+ }
+
+ Component.onCompleted: {
+ p2poolManager.p2poolStats.connect(updateMiningStats);
+ }
+}
diff --git a/qml.qrc b/qml.qrc
index 4915d4ae43..549723a860 100644
--- a/qml.qrc
+++ b/qml.qrc
@@ -26,6 +26,7 @@
pages/History.qml
pages/AddressBook.qml
pages/Mining.qml
+ pages/MiningStats.qml
components/ContextMenu.qml
components/ContextMenuItem.qml
components/NetworkStatusItem.qml
diff --git a/src/p2pool/P2PoolManager.cpp b/src/p2pool/P2PoolManager.cpp
index 0a035d5a75..4519566b30 100644
--- a/src/p2pool/P2PoolManager.cpp
+++ b/src/p2pool/P2PoolManager.cpp
@@ -151,6 +151,24 @@ void P2PoolManager::getStatus() {
return;
}
+void P2PoolManager::getStats()
+{
+ QVariantMap statsMap;
+ QString statsPath = m_p2poolPath + "/stats";
+ QDirIterator it(statsPath, QDir::Filter::Files, QDirIterator::Subdirectories);
+ while (it.hasNext()) {
+ QFile statsFile(it.next());
+ statsFile.open(QIODevice::ReadOnly);
+ QTextStream statsOut(&statsFile);
+ QByteArray data;
+ statsOut >> data;
+ statsFile.close();
+ QVariantMap jsonMap = QJsonDocument::fromJson(data).object().toVariantMap();
+ statsMap.insert(jsonMap);
+ }
+ emit p2poolStats(statsMap);
+}
+
bool P2PoolManager::start(const QString &flags, const QString &address, const QString &chain, const QString &threads)
{
// prepare command line arguments and pass to p2pool
diff --git a/src/p2pool/P2PoolManager.h b/src/p2pool/P2PoolManager.h
index b813e042ab..b0c1ead84a 100644
--- a/src/p2pool/P2PoolManager.h
+++ b/src/p2pool/P2PoolManager.h
@@ -50,6 +50,7 @@ class P2PoolManager : public QObject
Q_INVOKABLE void exit();
Q_INVOKABLE bool isInstalled();
Q_INVOKABLE void getStatus();
+ Q_INVOKABLE void getStats();
Q_INVOKABLE void download();
enum DownloadError {
@@ -68,6 +69,7 @@ class P2PoolManager : public QObject
void p2poolStatus(bool isMining, int hashrate) const;
void p2poolDownloadFailure(int errorCode) const;
void p2poolDownloadSuccess() const;
+ void p2poolStats(QVariantMap statsMap) const;
private:
std::unique_ptr m_p2poold;