Skip to content

Commit e5b3ab8

Browse files
qt: mempool stats chart
Co-authored-by: Jonas Schnelli <dev@jonasschnelli.ch> Co-authored-by: @RandyMcMillan <randy.lee.mcmillan@gmail.com>
1 parent d22e7ee commit e5b3ab8

File tree

11 files changed

+548
-6
lines changed

11 files changed

+548
-6
lines changed

src/Makefile.qt.include

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ QT_FORMS_UI = \
2222
qt/forms/helpmessagedialog.ui \
2323
qt/forms/intro.ui \
2424
qt/forms/modaloverlay.ui \
25+
qt/forms/mempoolstats.ui \
2526
qt/forms/openuridialog.ui \
2627
qt/forms/optionsdialog.ui \
2728
qt/forms/overviewpage.ui \
@@ -54,6 +55,7 @@ QT_MOC_CPP = \
5455
qt/moc_intro.cpp \
5556
qt/moc_macdockiconhandler.cpp \
5657
qt/moc_macnotificationhandler.cpp \
58+
qt/moc_mempoolstats.cpp \
5759
qt/moc_modaloverlay.cpp \
5860
qt/moc_notificator.cpp \
5961
qt/moc_openuridialog.cpp \
@@ -126,6 +128,7 @@ BITCOIN_QT_H = \
126128
qt/macdockiconhandler.h \
127129
qt/macnotificationhandler.h \
128130
qt/macos_appnap.h \
131+
qt/mempoolstats.h \
129132
qt/modaloverlay.h \
130133
qt/networkstyle.h \
131134
qt/notificator.h \
@@ -228,6 +231,7 @@ BITCOIN_QT_BASE_CPP = \
228231
qt/csvmodelwriter.cpp \
229232
qt/guiutil.cpp \
230233
qt/intro.cpp \
234+
qt/mempoolstats.cpp \
231235
qt/modaloverlay.cpp \
232236
qt/networkstyle.cpp \
233237
qt/notificator.cpp \

src/interfaces/node.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,24 @@ struct BlockAndHeaderTipInfo
4949
double verification_progress;
5050
};
5151

52+
class mempool_feeinfo {
53+
public:
54+
uint64_t total_size;
55+
uint64_t total_fee;
56+
uint64_t tx_count;
57+
CAmount fee_from;
58+
CAmount fee_to;
59+
60+
//TODO: remove
61+
// added for storing and loading a mempool set during development to avoid waiting hours for collecting enought samples
62+
SERIALIZE_METHODS(mempool_feeinfo, obj)
63+
{
64+
READWRITE(obj.total_size, obj.total_fee, obj.tx_count, obj.fee_from, obj.fee_to);
65+
}
66+
};
67+
68+
typedef std::vector<mempool_feeinfo> mempool_feehistogram;
69+
5270
//! Top-level interface for a bitcoin node (bitcoind process).
5371
class Node
5472
{
@@ -122,6 +140,9 @@ class Node
122140
//! Get mempool dynamic usage.
123141
virtual size_t getMempoolDynamicUsage() = 0;
124142

143+
//! Get mempool fee histogram
144+
virtual mempool_feehistogram getMempoolFeeHistogram() = 0;
145+
125146
//! Get header tip height and time.
126147
virtual bool getHeaderTip(int& height, int64_t& block_time) = 0;
127148

src/node/interfaces.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,58 @@ class NodeImpl : public Node
174174
int64_t getTotalBytesSent() override { return m_context->connman ? m_context->connman->GetTotalBytesSent() : 0; }
175175
size_t getMempoolSize() override { return m_context->mempool ? m_context->mempool->size() : 0; }
176176
size_t getMempoolDynamicUsage() override { return m_context->mempool ? m_context->mempool->DynamicMemoryUsage() : 0; }
177+
interfaces::mempool_feehistogram getMempoolFeeHistogram() override {
178+
/* TODO: define log scale formular for dynamically creating the
179+
* feelimits but with the property of not constantly changing
180+
* (and thus screw up client implementations) */
181+
static const std::vector<CAmount> feelimits{1, 2, 3, 4, 5, 6, 7, 8, 10,
182+
12, 14, 17, 20, 25, 30, 40, 50, 60, 70, 80, 100,
183+
120, 140, 170, 200, 250, 300, 400, 500, 600, 700, 800, 1000,
184+
1200, 1400, 1700, 2000, 2500, 3000, 4000, 5000, 6000, 7000, 8000, 10000};
185+
186+
/* keep histogram per...
187+
* ... cumulated tx sizes
188+
* ... txns (count)
189+
* ... cumulated fees */
190+
std::vector<uint64_t> sizes(feelimits.size(), 0);
191+
std::vector<uint64_t> count(feelimits.size(), 0);
192+
std::vector<uint64_t> fees(feelimits.size(), 0);
193+
194+
{
195+
LOCK(m_context->mempool->cs);
196+
for (const CTxMemPoolEntry& e : m_context->mempool->mapTx) {
197+
int size = (int)e.GetTxSize();
198+
CAmount fee = e.GetFee();
199+
uint64_t asize = e.GetSizeWithAncestors();
200+
CAmount afees = e.GetModFeesWithAncestors();
201+
uint64_t dsize = e.GetSizeWithDescendants();
202+
CAmount dfees = e.GetModFeesWithDescendants();
203+
204+
CAmount fpb = fee / size; //fee per byte
205+
CAmount afpb = afees / asize; //fee per byte including ancestors
206+
CAmount dfpb = dfees / dsize; //fee per byte including descendants
207+
CAmount tfpb = (afees + dfees - fee) / (asize + dsize - size);
208+
CAmount feeperbyte = std::max(std::min(dfpb, tfpb), std::min(fpb, afpb));
209+
210+
// distribute feerates into feelimits
211+
for (size_t i = 0; i < feelimits.size(); i++) {
212+
if (feeperbyte >= feelimits[i] && (i == feelimits.size() - 1 || feeperbyte < feelimits[i + 1])) {
213+
sizes[i] += size;
214+
count[i]++;
215+
fees[i] += fee;
216+
break;
217+
}
218+
}
219+
}
220+
}
221+
interfaces::mempool_feehistogram feeinfo;
222+
for (size_t i = 0; i < feelimits.size(); i++) {
223+
feeinfo.push_back({sizes[i], fees[i], count[i], feelimits[i], (i == feelimits.size() - 1 ? std::numeric_limits<int64_t>::max() : feelimits[i + 1])});
224+
}
225+
226+
return feeinfo;
227+
}
228+
177229
bool getHeaderTip(int& height, int64_t& block_time) override
178230
{
179231
LOCK(::cs_main);

src/qt/clientmodel.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <clientversion.h>
1414
#include <interfaces/handler.h>
1515
#include <interfaces/node.h>
16+
#include <memusage.h>
1617
#include <net.h>
1718
#include <netbase.h>
1819
#include <util/system.h>
@@ -23,6 +24,7 @@
2324
#include <functional>
2425

2526
#include <QDebug>
27+
#include <QMutexLocker>
2628
#include <QThread>
2729
#include <QTimer>
2830

@@ -51,6 +53,19 @@ ClientModel::ClientModel(interfaces::Node& node, OptionsModel *_optionsModel, QO
5153
connect(timer, &QTimer::timeout, [this] {
5254
// no locking required at this point
5355
// the following calls will acquire the required lock
56+
57+
int64_t now = GetTime();
58+
if (m_mempool_feehist_last_sample_timestamp == 0 || static_cast<uint64_t>(m_mempool_feehist_last_sample_timestamp)+static_cast<uint64_t>(m_mempool_collect_intervall) < static_cast<uint64_t>(now)) {
59+
QMutexLocker locker(&m_mempool_locker);
60+
interfaces::mempool_feehistogram fee_histogram = m_node.getMempoolFeeHistogram();
61+
m_mempool_feehist.push_back({now, fee_histogram});
62+
if (m_mempool_feehist.size() > m_mempool_max_samples) {
63+
m_mempool_feehist.erase(m_mempool_feehist.begin(), m_mempool_feehist.begin()+1);
64+
}
65+
m_mempool_feehist_last_sample_timestamp = now;
66+
Q_EMIT mempoolFeeHistChanged();
67+
}
68+
5469
Q_EMIT mempoolSizeChanged(m_node.getMempoolSize(), m_node.getMempoolDynamicUsage());
5570
Q_EMIT bytesChanged(m_node.getTotalBytesRecv(), m_node.getTotalBytesSent());
5671
});

src/qt/clientmodel.h

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#ifndef BITCOIN_QT_CLIENTMODEL_H
66
#define BITCOIN_QT_CLIENTMODEL_H
77

8+
#include <QMutex>
89
#include <QObject>
910
#include <QDateTime>
1011

@@ -13,18 +14,15 @@
1314
#include <sync.h>
1415
#include <uint256.h>
1516

17+
#include <interfaces/node.h>
18+
1619
class BanTableModel;
1720
class CBlockIndex;
1821
class OptionsModel;
1922
class PeerTableModel;
2023
class PeerTableSortProxy;
2124
enum class SynchronizationState;
2225

23-
namespace interfaces {
24-
class Handler;
25-
class Node;
26-
}
27-
2826
QT_BEGIN_NAMESPACE
2927
class QTimer;
3028
QT_END_NAMESPACE
@@ -87,6 +85,13 @@ class ClientModel : public QObject
8785
Mutex m_cached_tip_mutex;
8886
uint256 m_cached_tip_blocks GUARDED_BY(m_cached_tip_mutex){};
8987

88+
typedef std::pair<int64_t, std::vector<interfaces::mempool_feeinfo>> mempool_feehist_sample; //!< sample plus timestamp
89+
mutable QMutex m_mempool_locker;
90+
const static size_t m_mempool_max_samples{540};
91+
const static size_t m_mempool_collect_intervall{20}; // 540*20 = 3h of sample window
92+
std::vector<mempool_feehist_sample> m_mempool_feehist;
93+
std::atomic<int64_t> m_mempool_feehist_last_sample_timestamp{0};
94+
9095
private:
9196
interfaces::Node& m_node;
9297
std::unique_ptr<interfaces::Handler> m_handler_show_progress;
@@ -111,6 +116,7 @@ class ClientModel : public QObject
111116
void numConnectionsChanged(int count);
112117
void numBlocksChanged(int count, const QDateTime& blockDate, double nVerificationProgress, bool header, SynchronizationState sync_state);
113118
void mempoolSizeChanged(long count, size_t mempoolSizeInBytes);
119+
void mempoolFeeHistChanged();
114120
void networkActiveChanged(bool networkActive);
115121
void alertsChanged(const QString &warnings);
116122
void bytesChanged(quint64 totalBytesIn, quint64 totalBytesOut);

src/qt/forms/debugwindow.ui

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -891,6 +891,44 @@
891891
</item>
892892
</layout>
893893
</widget>
894+
<widget class="QWidget" name="tab_mempool">
895+
<property name="minimumSize">
896+
<size>
897+
<width>747</width>
898+
<height>0</height>
899+
</size>
900+
</property>
901+
<attribute name="title">
902+
<string>Mempool</string>
903+
</attribute>
904+
<layout class="QVBoxLayout" name="verticalLayout_9" stretch="0">
905+
<property name="leftMargin">
906+
<number>0</number>
907+
</property>
908+
<property name="topMargin">
909+
<number>0</number>
910+
</property>
911+
<property name="rightMargin">
912+
<number>0</number>
913+
</property>
914+
<property name="bottomMargin">
915+
<number>0</number>
916+
</property>
917+
<item>
918+
<widget class="MempoolStats" name="mempool_graph" native="true">
919+
<property name="sizePolicy">
920+
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
921+
<horstretch>0</horstretch>
922+
<verstretch>0</verstretch>
923+
</sizepolicy>
924+
</property>
925+
<property name="styleSheet">
926+
<string notr="true">QScrollBar {height:0px;}</string>
927+
</property>
928+
</widget>
929+
</item>
930+
</layout>
931+
</widget>
894932
<widget class="QWidget" name="tab_peers">
895933
<attribute name="title">
896934
<string>&amp;Peers</string>
@@ -1655,6 +1693,12 @@
16551693
<slot>clear()</slot>
16561694
</slots>
16571695
</customwidget>
1696+
<customwidget>
1697+
<class>MempoolStats</class>
1698+
<extends>QWidget</extends>
1699+
<header>qt/mempoolstats.h</header>
1700+
<container>1</container>
1701+
</customwidget>
16581702
</customwidgets>
16591703
<resources>
16601704
<include location="../bitcoin.qrc"/>

src/qt/forms/mempoolstats.ui

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<ui version="4.0">
3+
<class>MempoolStats</class>
4+
<widget class="QWidget" name="MempoolStats">
5+
<property name="geometry">
6+
<rect>
7+
<x>0</x>
8+
<y>0</y>
9+
<width>800</width>
10+
<height>640</height>
11+
</rect>
12+
</property>
13+
<property name="minimumSize">
14+
<size>
15+
<width>800</width>
16+
<height>640</height>
17+
</size>
18+
</property>
19+
<property name="windowTitle">
20+
<string>Mempool Stats</string>
21+
</property>
22+
<layout class="QHBoxLayout" name="horizontalLayout">
23+
<property name="leftMargin">
24+
<number>0</number>
25+
</property>
26+
<property name="topMargin">
27+
<number>0</number>
28+
</property>
29+
<property name="rightMargin">
30+
<number>0</number>
31+
</property>
32+
<property name="bottomMargin">
33+
<number>0</number>
34+
</property>
35+
<item>
36+
<widget class="QGraphicsView" name="graphicsView">
37+
<property name="horizontalScrollBarPolicy">
38+
<enum>Qt::ScrollBarAlwaysOff</enum>
39+
</property>
40+
</widget>
41+
</item>
42+
</layout>
43+
</widget>
44+
<resources/>
45+
<connections/>
46+
</ui>

0 commit comments

Comments
 (0)