diff --git a/Controller.cpp b/Controller.cpp index 3cd51b5..0eb79f8 100644 --- a/Controller.cpp +++ b/Controller.cpp @@ -139,6 +139,20 @@ void Controller::startup() conns += connect(bitcoindmgr.get(), &BitcoinDMgr::allConnectionsLost, this, [this]{ stopTimer(mempoolLogTimer);}); } + { + // set up periodid refresh of mempool fee histogram + constexpr const char *feeHistogramTimer = "feeHistogramTimer"; + constexpr int feeHistogramTimerInterval = 10 * 1000;//500 * 1000; // 500 seconds + conns += connect(this, &Controller::upToDate, this, [this] { + callOnTimerSoon(feeHistogramTimerInterval, feeHistogramTimer, [this]{ + refreshMempoolHistogram(); + return true; + }, false, Qt::TimerType::VeryCoarseTimer); + }); + // disable the timer if synchronizing and restart it later when up-to-date + conns += connect(this, &Controller::synchronizing, this, [this]{ stopTimer(feeHistogramTimer); }); + } + start(); // start our thread } @@ -1172,6 +1186,23 @@ void Controller::process_DoUndoAndRetry() } } +void Controller::refreshMempoolHistogram() const +{ + const auto t0 = Util::getTimeMicros(); + Mempool::FeeHistogramVec hist; + { + auto [mempool, lock] = storage->mempool(); + auto histtmp = mempool.calcCompactFeeHistogram(); + hist.swap(histtmp); + } + const auto elapsed = Util::getTimeMicros() - t0; + Debug() << "Histogram: " << Util::Stringify(hist, [](const auto &val) { + auto & [feeRate, cumSize] = val; + return QString("(%1, %2)").arg(feeRate).arg(cumSize); + }); + Debug() << "Refreshed mempool fee histogram in " << QString::number(elapsed/1e3, 'f', 4) << " msec"; +} + // -- CtlTask CtlTask::CtlTask(Controller *ctl, const QString &name) : QObject(nullptr), ctl(ctl) diff --git a/Controller.h b/Controller.h index db2a668..a6c83d4 100644 --- a/Controller.h +++ b/Controller.h @@ -148,6 +148,9 @@ private: /// takes locks, prints to Log() every 30 seconds if there were changes void printMempoolStatusToLog() const; + + /// called from a timer every 500 seconds -- takes locks, updates compact fee histogram + void refreshMempoolHistogram() const; }; /// Abstract base class for our private internal tasks. Concrete implementations are in Controller.cpp. diff --git a/Fulcrum.pro b/Fulcrum.pro index 85e8205..9fd774e 100644 --- a/Fulcrum.pro +++ b/Fulcrum.pro @@ -146,6 +146,7 @@ SOURCES += \ Common.cpp \ Controller.cpp \ Logger.cpp \ + Mempool.cpp \ main.cpp \ Merkle.cpp \ Mixins.cpp \ diff --git a/Mempool.cpp b/Mempool.cpp new file mode 100644 index 0000000..34a15a3 --- /dev/null +++ b/Mempool.cpp @@ -0,0 +1,35 @@ +#include "Mempool.h" + +#include + +#include +#include + +auto Mempool::calcCompactFeeHistogram(double binSize) const -> FeeHistogramVec +{ + FeeHistogramVec ret; + std::map> histogram; // sorted map, descending order by key + + for (const auto & [txid, tx] : txs) { + const auto feeRate = unsigned(tx->fee / bitcoin::Amount::satoshi()) // sats + / std::max(tx->sizeBytes, 1u); // per byte + histogram[feeRate] += tx->sizeBytes; // accumulate size by feeRate + } + + // now, compact the bins + ret.reserve(8); + unsigned cumSize = 0; + double r = 0.; + + for (const auto & [feeRate, size] : histogram) { + cumSize += size; + if (cumSize + r > binSize) { + ret.emplace_back(FeeHistogramItem{feeRate, cumSize}); + r += double(cumSize) - binSize; + cumSize = 0; + binSize *= 1.1; + } + } + ret.shrink_to_fit(); // save memory + return ret; +} diff --git a/Mempool.h b/Mempool.h index 6c0439a..0e6fac3 100644 --- a/Mempool.h +++ b/Mempool.h @@ -131,4 +131,16 @@ struct Mempool hashXTxs.reserve(size_t(hxSize*0.75)); nextOrdinal = 0; } + + // -- Fee histogram support (used by mempool.get_fee_histogram RPC) -- + + struct FeeHistogramItem { + unsigned feeRate = 0; // in sats/B, quotient truncated to uint. + unsigned cumulativeSize = 0; // bin size, cumulative bytes + }; + using FeeHistogramVec = std::vector; + /// This function is potentially going to take a few ms (or more) on large mempools, so call this from a background + /// task that updates a cached histogram. If calling this from threaded code with a shared mempool, be sure to call + /// it with the appropriate locks held. + FeeHistogramVec calcCompactFeeHistogram(double binSize = 1e5 /* binSize in bytes */) const; };