Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: mempool stats chart #108

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/Makefile.qt.include
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ QT_FORMS_UI = \
qt/forms/helpmessagedialog.ui \
qt/forms/intro.ui \
qt/forms/modaloverlay.ui \
qt/forms/mempoolstats.ui \
qt/forms/openuridialog.ui \
qt/forms/optionsdialog.ui \
qt/forms/overviewpage.ui \
Expand Down Expand Up @@ -54,6 +55,7 @@ QT_MOC_CPP = \
qt/moc_intro.cpp \
qt/moc_macdockiconhandler.cpp \
qt/moc_macnotificationhandler.cpp \
qt/moc_mempoolstats.cpp \
qt/moc_modaloverlay.cpp \
qt/moc_notificator.cpp \
qt/moc_openuridialog.cpp \
Expand Down Expand Up @@ -124,6 +126,7 @@ BITCOIN_QT_H = \
qt/macdockiconhandler.h \
qt/macnotificationhandler.h \
qt/macos_appnap.h \
qt/mempoolstats.h \
qt/modaloverlay.h \
qt/networkstyle.h \
qt/notificator.h \
Expand Down Expand Up @@ -221,6 +224,7 @@ BITCOIN_QT_BASE_CPP = \
qt/csvmodelwriter.cpp \
qt/guiutil.cpp \
qt/intro.cpp \
qt/mempoolstats.cpp \
qt/modaloverlay.cpp \
qt/networkstyle.cpp \
qt/notificator.cpp \
Expand Down
51 changes: 51 additions & 0 deletions src/interfaces/node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,57 @@ class NodeImpl : public Node
int64_t getTotalBytesSent() override { return m_context->connman ? m_context->connman->GetTotalBytesSent() : 0; }
size_t getMempoolSize() override { return m_context->mempool ? m_context->mempool->size() : 0; }
size_t getMempoolDynamicUsage() override { return m_context->mempool ? m_context->mempool->DynamicMemoryUsage() : 0; }
mempool_feehistogram getMempoolFeeHistogram() override {
/* TODO: define log scale formular for dynamically creating the
* feelimits but with the property of not constantly changing
* (and thus screw up client implementations) */
static const std::vector<CAmount> feelimits{1, 2, 3, 4, 5, 6, 7, 8, 10,
12, 14, 17, 20, 25, 30, 40, 50, 60, 70, 80, 100,
120, 140, 170, 200, 250, 300, 400, 500, 600, 700, 800, 1000,
1200, 1400, 1700, 2000, 2500, 3000, 4000, 5000, 6000, 7000, 8000, 10000};

/* keep histogram per...
* ... cumulated tx sizes
* ... txns (count)
* ... cumulated fees */
std::vector<uint64_t> sizes(feelimits.size(), 0);
std::vector<uint64_t> count(feelimits.size(), 0);
std::vector<uint64_t> fees(feelimits.size(), 0);

{
LOCK(m_context->mempool->cs);
for (const CTxMemPoolEntry& e : m_context->mempool->mapTx) {
int size = (int)e.GetTxSize();
CAmount fee = e.GetFee();
uint64_t asize = e.GetSizeWithAncestors();
CAmount afees = e.GetModFeesWithAncestors();
uint64_t dsize = e.GetSizeWithDescendants();
CAmount dfees = e.GetModFeesWithDescendants();

CAmount fpb = fee / size; //fee per byte
CAmount afpb = afees / asize; //fee per byte including ancestors
CAmount dfpb = dfees / dsize; //fee per byte including descendants
CAmount tfpb = (afees + dfees - fee) / (asize + dsize - size);
CAmount feeperbyte = std::max(std::min(dfpb, tfpb), std::min(fpb, afpb));

// distribute feerates into feelimits
for (size_t i = 0; i < feelimits.size(); i++) {
if (feeperbyte >= feelimits[i] && (i == feelimits.size() - 1 || feeperbyte < feelimits[i + 1])) {
sizes[i] += size;
count[i]++;
fees[i] += fee;
break;
}
}
}
}
mempool_feehistogram feeinfo;
for (size_t i = 0; i < feelimits.size(); i++) {
feeinfo.push_back({sizes[i], fees[i], count[i], feelimits[i], (i == feelimits.size() - 1 ? std::numeric_limits<int64_t>::max() : feelimits[i + 1])});
}

return feeinfo;
}
bool getHeaderTip(int& height, int64_t& block_time) override
{
LOCK(::cs_main);
Expand Down
21 changes: 21 additions & 0 deletions src/interfaces/node.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,24 @@ struct BlockAndHeaderTipInfo
double verification_progress;
};

class mempool_feeinfo {
public:
uint64_t total_size;
uint64_t total_fee;
uint64_t tx_count;
CAmount fee_from;
CAmount fee_to;

//TODO: remove
// added for storing and loading a mempool set during development to avoid waiting hours for collecting enought samples
SERIALIZE_METHODS(mempool_feeinfo, obj)
{
READWRITE(obj.total_size, obj.total_fee, obj.tx_count, obj.fee_from, obj.fee_to);
}
};

typedef std::vector<mempool_feeinfo> mempool_feehistogram;

//! Top-level interface for a bitcoin node (bitcoind process).
class Node
{
Expand Down Expand Up @@ -121,6 +139,9 @@ class Node
//! Get mempool dynamic usage.
virtual size_t getMempoolDynamicUsage() = 0;

//! Get mempool fee histogram
virtual mempool_feehistogram getMempoolFeeHistogram() = 0;

//! Get header tip height and time.
virtual bool getHeaderTip(int& height, int64_t& block_time) = 0;

Expand Down
15 changes: 15 additions & 0 deletions src/qt/clientmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <clientversion.h>
#include <interfaces/handler.h>
#include <interfaces/node.h>
#include <memusage.h>
#include <net.h>
#include <netbase.h>
#include <util/system.h>
Expand All @@ -21,6 +22,7 @@
#include <stdint.h>

#include <QDebug>
#include <QMutexLocker>
#include <QThread>
#include <QTimer>

Expand All @@ -45,6 +47,19 @@ ClientModel::ClientModel(interfaces::Node& node, OptionsModel *_optionsModel, QO
connect(timer, &QTimer::timeout, [this] {
// no locking required at this point
// the following calls will acquire the required lock

int64_t now = GetTime();
if (m_mempool_feehist_last_sample_timestamp == 0 || m_mempool_feehist_last_sample_timestamp+m_mempool_collect_intervall < static_cast<uint64_t>(now)) {
QMutexLocker locker(&m_mempool_locker);
interfaces::mempool_feehistogram fee_histogram = m_node.getMempoolFeeHistogram();
m_mempool_feehist.push_back({now, fee_histogram});
if (m_mempool_feehist.size() > m_mempool_max_samples) {
m_mempool_feehist.erase(m_mempool_feehist.begin(), m_mempool_feehist.begin()+1);
}
m_mempool_feehist_last_sample_timestamp = now;
Q_EMIT mempoolFeeHistChanged();
}

Q_EMIT mempoolSizeChanged(m_node.getMempoolSize(), m_node.getMempoolDynamicUsage());
Q_EMIT bytesChanged(m_node.getTotalBytesRecv(), m_node.getTotalBytesSent());
});
Expand Down
16 changes: 11 additions & 5 deletions src/qt/clientmodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#ifndef BITCOIN_QT_CLIENTMODEL_H
#define BITCOIN_QT_CLIENTMODEL_H

#include <QMutex>
#include <QObject>
#include <QDateTime>

Expand All @@ -13,17 +14,14 @@
#include <sync.h>
#include <uint256.h>

#include <interfaces/node.h>

class BanTableModel;
class CBlockIndex;
class OptionsModel;
class PeerTableModel;
enum class SynchronizationState;

namespace interfaces {
class Handler;
class Node;
}

QT_BEGIN_NAMESPACE
class QTimer;
QT_END_NAMESPACE
Expand Down Expand Up @@ -85,6 +83,13 @@ class ClientModel : public QObject
Mutex m_cached_tip_mutex;
uint256 m_cached_tip_blocks GUARDED_BY(m_cached_tip_mutex){};

typedef std::pair<int64_t, std::vector<interfaces::mempool_feeinfo>> mempool_feehist_sample; //!< sample plus timestamp
mutable QMutex m_mempool_locker;
const static size_t m_mempool_max_samples{540};
const static size_t m_mempool_collect_intervall{20}; // 540*20 = 3h of sample window
std::vector<mempool_feehist_sample> m_mempool_feehist;
std::atomic<int64_t> m_mempool_feehist_last_sample_timestamp{0};

private:
interfaces::Node& m_node;
std::unique_ptr<interfaces::Handler> m_handler_show_progress;
Expand All @@ -108,6 +113,7 @@ class ClientModel : public QObject
void numConnectionsChanged(int count);
void numBlocksChanged(int count, const QDateTime& blockDate, double nVerificationProgress, bool header, SynchronizationState sync_state);
void mempoolSizeChanged(long count, size_t mempoolSizeInBytes);
void mempoolFeeHistChanged();
void networkActiveChanged(bool networkActive);
void alertsChanged(const QString &warnings);
void bytesChanged(quint64 totalBytesIn, quint64 totalBytesOut);
Expand Down
44 changes: 44 additions & 0 deletions src/qt/forms/debugwindow.ui
Original file line number Diff line number Diff line change
Expand Up @@ -917,6 +917,44 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_mempool">
<property name="minimumSize">
<size>
<width>747</width>
<height>0</height>
</size>
</property>
<attribute name="title">
<string>Mempool</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_9" stretch="0">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="MempoolStats" name="mempool_graph" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_peers">
<attribute name="title">
<string>&amp;Peers</string>
Expand Down Expand Up @@ -1536,6 +1574,12 @@
<slot>clear()</slot>
</slots>
</customwidget>
<customwidget>
<class>MempoolStats</class>
<extends>QWidget</extends>
<header>qt/mempoolstats.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources>
<include location="../bitcoin.qrc"/>
Expand Down
46 changes: 46 additions & 0 deletions src/qt/forms/mempoolstats.ui
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MempoolStats</class>
<widget class="QWidget" name="MempoolStats">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>640</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>800</width>
<height>640</height>
</size>
</property>
<property name="windowTitle">
<string>Mempool Stats</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QGraphicsView" name="graphicsView">
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
Loading