Skip to content

Commit 96ee92a

Browse files
committed
WIP: mempool stats chart
1 parent f5bd46a commit 96ee92a

File tree

10 files changed

+544
-5
lines changed

10 files changed

+544
-5
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 \
@@ -124,6 +126,7 @@ BITCOIN_QT_H = \
124126
qt/macdockiconhandler.h \
125127
qt/macnotificationhandler.h \
126128
qt/macos_appnap.h \
129+
qt/mempoolstats.h \
127130
qt/modaloverlay.h \
128131
qt/networkstyle.h \
129132
qt/notificator.h \
@@ -221,6 +224,7 @@ BITCOIN_QT_BASE_CPP = \
221224
qt/csvmodelwriter.cpp \
222225
qt/guiutil.cpp \
223226
qt/intro.cpp \
227+
qt/mempoolstats.cpp \
224228
qt/modaloverlay.cpp \
225229
qt/networkstyle.cpp \
226230
qt/notificator.cpp \

src/interfaces/node.cpp

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

src/interfaces/node.h

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

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

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

src/qt/clientmodel.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <clientversion.h>
1313
#include <interfaces/handler.h>
1414
#include <interfaces/node.h>
15+
#include <memusage.h>
1516
#include <net.h>
1617
#include <netbase.h>
1718
#include <util/system.h>
@@ -21,6 +22,7 @@
2122
#include <stdint.h>
2223

2324
#include <QDebug>
25+
#include <QMutexLocker>
2426
#include <QThread>
2527
#include <QTimer>
2628

@@ -45,6 +47,19 @@ ClientModel::ClientModel(interfaces::Node& node, OptionsModel *_optionsModel, QO
4547
connect(timer, &QTimer::timeout, [this] {
4648
// no locking required at this point
4749
// the following calls will acquire the required lock
50+
51+
int64_t now = GetTime();
52+
if (m_mempool_feehist_last_sample_timestamp == 0 || m_mempool_feehist_last_sample_timestamp+m_mempool_collect_intervall < now) {
53+
QMutexLocker locker(&m_mempool_locker);
54+
interfaces::mempool_feehistogram fee_histogram = m_node.getMempoolFeeHistogram();
55+
m_mempool_feehist.push_back({now, fee_histogram});
56+
if (m_mempool_feehist.size() > m_mempool_max_samples) {
57+
m_mempool_feehist.erase(m_mempool_feehist.begin(), m_mempool_feehist.begin()+1);
58+
}
59+
m_mempool_feehist_last_sample_timestamp = now;
60+
Q_EMIT mempoolFeeHistChanged();
61+
}
62+
4863
Q_EMIT mempoolSizeChanged(m_node.getMempoolSize(), m_node.getMempoolDynamicUsage());
4964
Q_EMIT bytesChanged(m_node.getTotalBytesRecv(), m_node.getTotalBytesSent());
5065
});

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,17 +14,14 @@
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
enum class SynchronizationState;
2124

22-
namespace interfaces {
23-
class Handler;
24-
class Node;
25-
}
26-
2725
QT_BEGIN_NAMESPACE
2826
class QTimer;
2927
QT_END_NAMESPACE
@@ -85,6 +83,13 @@ class ClientModel : public QObject
8583
Mutex m_cached_tip_mutex;
8684
uint256 m_cached_tip_blocks GUARDED_BY(m_cached_tip_mutex){};
8785

86+
typedef std::pair<int64_t, std::vector<interfaces::mempool_feeinfo>> mempool_feehist_sample; //!< sample plus timestamp
87+
mutable QMutex m_mempool_locker;
88+
const static size_t m_mempool_max_samples{540};
89+
const static size_t m_mempool_collect_intervall{20}; // 540*20 = 3h of sample window
90+
std::vector<mempool_feehist_sample> m_mempool_feehist;
91+
std::atomic<int64_t> m_mempool_feehist_last_sample_timestamp{0};
92+
8893
private:
8994
interfaces::Node& m_node;
9095
std::unique_ptr<interfaces::Handler> m_handler_show_progress;
@@ -108,6 +113,7 @@ class ClientModel : public QObject
108113
void numConnectionsChanged(int count);
109114
void numBlocksChanged(int count, const QDateTime& blockDate, double nVerificationProgress, bool header, SynchronizationState sync_state);
110115
void mempoolSizeChanged(long count, size_t mempoolSizeInBytes);
116+
void mempoolFeeHistChanged();
111117
void networkActiveChanged(bool networkActive);
112118
void alertsChanged(const QString &warnings);
113119
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
@@ -917,6 +917,44 @@
917917
</item>
918918
</layout>
919919
</widget>
920+
<widget class="QWidget" name="tab_mempool">
921+
<property name="minimumSize">
922+
<size>
923+
<width>747</width>
924+
<height>0</height>
925+
</size>
926+
</property>
927+
<attribute name="title">
928+
<string>Mempool</string>
929+
</attribute>
930+
<layout class="QVBoxLayout" name="verticalLayout_9" stretch="0">
931+
<property name="leftMargin">
932+
<number>0</number>
933+
</property>
934+
<property name="topMargin">
935+
<number>0</number>
936+
</property>
937+
<property name="rightMargin">
938+
<number>0</number>
939+
</property>
940+
<property name="bottomMargin">
941+
<number>0</number>
942+
</property>
943+
<item>
944+
<widget class="MempoolStats" name="mempool_graph" native="true">
945+
<property name="sizePolicy">
946+
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
947+
<horstretch>0</horstretch>
948+
<verstretch>0</verstretch>
949+
</sizepolicy>
950+
</property>
951+
<property name="styleSheet">
952+
<string notr="true"/>
953+
</property>
954+
</widget>
955+
</item>
956+
</layout>
957+
</widget>
920958
<widget class="QWidget" name="tab_peers">
921959
<attribute name="title">
922960
<string>&amp;Peers</string>
@@ -1536,6 +1574,12 @@
15361574
<slot>clear()</slot>
15371575
</slots>
15381576
</customwidget>
1577+
<customwidget>
1578+
<class>MempoolStats</class>
1579+
<extends>QWidget</extends>
1580+
<header>qt/mempoolstats.h</header>
1581+
<container>1</container>
1582+
</customwidget>
15391583
</customwidgets>
15401584
<resources>
15411585
<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)