Skip to content

Commit

Permalink
WIP: mempool stats chart
Browse files Browse the repository at this point in the history
  • Loading branch information
jonasschnelli committed Oct 24, 2020
1 parent f5bd46a commit bc78567
Show file tree
Hide file tree
Showing 10 changed files with 544 additions and 5 deletions.
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

0 comments on commit bc78567

Please sign in to comment.