Skip to content

Commit

Permalink
[policy, fees]: add last 6 blocks fee estimate forecaster
Browse files Browse the repository at this point in the history
  • Loading branch information
ismaelsadeeq committed May 7, 2024
1 parent 061b6da commit a15cffd
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 1 deletion.
2 changes: 2 additions & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ BITCOIN_CORE_H = \
policy/fees_args.h \
policy/packages.h \
policy/mempool_fees.h \
policy/block_fees.h \
policy/policy.h \
policy/rbf.h \
policy/settings.h \
Expand Down Expand Up @@ -447,6 +448,7 @@ libbitcoin_node_a_SOURCES = \
policy/fees.cpp \
policy/fees_args.cpp \
policy/mempool_fees.cpp \
policy/block_fees.cpp \
policy/packages.cpp \
policy/rbf.cpp \
policy/settings.cpp \
Expand Down
5 changes: 4 additions & 1 deletion src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
#include <node/miner.h>
#include <node/peerman_args.h>
#include <node/validation_cache_args.h>
#include <policy/block_fees.h>
#include <policy/estimator.h>
#include <policy/feerate.h>
#include <policy/fees.h>
Expand Down Expand Up @@ -1640,7 +1641,9 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
ChainstateManager& chainman = *Assert(node.chainman);

node.fee_estimator->RegisterForecaster(std::make_unique<MemPoolPolicyEstimator>(node.mempool.get(), &(chainman.ActiveChainstate())));

auto block_estimator = std::make_shared<BlockFees>();
validation_signals.RegisterValidationInterface(block_estimator.get());
node.fee_estimator->RegisterForecaster(block_estimator);
assert(!node.peerman);
node.peerman = PeerManager::make(*node.connman, *node.addrman,
node.banman.get(), chainman,
Expand Down
72 changes: 72 additions & 0 deletions src/policy/block_fees.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright (c) 2024 The Bitcoin Core developers
// Distributed under the MIT software license. See the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <kernel/mempool_entry.h>
#include <logging.h>
#include <policy/block_fees.h>
#include <policy/estimator.h>
#include <policy/policy.h>
#include <queue>

void BlockFees::MempoolTransactionsRemovedForBlock(const std::vector<RemovedMempoolTransactionInfo>& txs_removed_for_block, unsigned int /*unused*/)
{
unsigned int block_weight = 0;
BlockPercentiles percentiles;

for (const auto& tx : txs_removed_for_block) {
block_weight += tx.info.m_virtual_transaction_size * WITNESS_SCALE_FACTOR;
auto fee_rate = CFeeRate(tx.info.m_fee, tx.info.m_virtual_transaction_size);
if (block_weight >= static_cast<unsigned int>(0.05 * DEFAULT_BLOCK_MAX_WEIGHT) && percentiles.p5 == CFeeRate(0)) {
percentiles.p5 = fee_rate;
}
if (block_weight >= static_cast<unsigned int>(0.25 * DEFAULT_BLOCK_MAX_WEIGHT) && percentiles.p25 == CFeeRate(0)) {
percentiles.p25 = fee_rate;
}
if (block_weight >= static_cast<unsigned int>(0.5 * DEFAULT_BLOCK_MAX_WEIGHT) && percentiles.p50 == CFeeRate(0)) {
percentiles.p50 = fee_rate;
}
if (block_weight >= static_cast<unsigned int>(0.75 * DEFAULT_BLOCK_MAX_WEIGHT) && percentiles.p75 == CFeeRate(0)) {
percentiles.p75 = fee_rate;
}
}

if (blocks_percentiles.size() == MAX_NUMBER_OF_BLOCKS) {
blocks_percentiles.pop();
}
blocks_percentiles.push(percentiles);
}

ForecastResult BlockFees::EstimateFee(const unsigned int& targetBlocks)
{
BlockPercentiles percentiles_average;

if (targetBlocks > BLOCK_MAX_CONF_TARGET) {
return ForecastResult(strprintf("Block Forecast: Confirmation target %u is above the maximum limit of %u. Mempool conditions might change, and estimates above %u are unreliable.",
targetBlocks, BLOCK_MAX_CONF_TARGET, BLOCK_MAX_CONF_TARGET));
}

if (blocks_percentiles.size() < MAX_NUMBER_OF_BLOCKS) {
return ForecastResult("Block Forecast: Insufficient block data to perform an estimate");
}

std::queue<BlockPercentiles> blocks_percentiles_cp = blocks_percentiles;
while (!blocks_percentiles_cp.empty()) {
const auto& curr_percentile = blocks_percentiles_cp.front();
blocks_percentiles_cp.pop();
percentiles_average.p5 += curr_percentile.p5;
percentiles_average.p25 += curr_percentile.p25;
percentiles_average.p50 += curr_percentile.p50;
percentiles_average.p75 += curr_percentile.p75;
}

percentiles_average.p5 = CFeeRate(percentiles_average.p5.GetFeePerK() / MAX_NUMBER_OF_BLOCKS);
percentiles_average.p25 = CFeeRate(percentiles_average.p25.GetFeePerK() / MAX_NUMBER_OF_BLOCKS);
percentiles_average.p50 = CFeeRate(percentiles_average.p50.GetFeePerK() / MAX_NUMBER_OF_BLOCKS);
percentiles_average.p75 = CFeeRate(percentiles_average.p75.GetFeePerK() / MAX_NUMBER_OF_BLOCKS);

LogInfo("Block Forecast: Next block 75th percentile fee rate %s %s/kvB, 50th percentile fee rate %s %s/kvB, 25th percentile fee rate %s %s/kvB, 5th percentile fee rate %s %s/kvB \n",
percentiles_average.p75.GetFeePerK(), CURRENCY_ATOM, percentiles_average.p50.GetFeePerK(), CURRENCY_ATOM, percentiles_average.p25.GetFeePerK(), CURRENCY_ATOM, percentiles_average.p5.GetFeePerK(), CURRENCY_ATOM);

return ForecastResult(percentiles_average.p25, percentiles_average.p50, "Block Forecast");
}
48 changes: 48 additions & 0 deletions src/policy/block_fees.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright (c) 2024 The Bitcoin Core developers
// Distributed under the MIT software license. See the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#ifndef BITCOIN_POLICY_BLOCK_FEES_H
#define BITCOIN_POLICY_BLOCK_FEES_H

#include <policy/forecaster.h>
#include <queue>
#include <validationinterface.h>

struct RemovedMempoolTransactionInfo;
class Forecaster;
class CValidationInterface;
struct ForecastResult;

static const int MAX_NUMBER_OF_BLOCKS = 6;
static const unsigned int BLOCK_MAX_CONF_TARGET{1};

/**
* BlockFees estimates the fee rate that a transaction will pay
* to be included in a block as soon as possible.
* BlockFees uses the fee rate of transactions that were confirmed in
* the last MAX_NUMBER_OF_BLOCKS blocks to calculate their percentiles
* and takes the average as the fee rate estimate.
* TODO: Don't just take individual fee rates; consider selecting the mining score when selecting
* percentile fee rates.
* TODO: Persist this data to disk and use it upon a quick restart where the last mined block is
* less than one.
*/
class BlockFees : public CValidationInterface, public Forecaster
{
private:
std::queue<BlockPercentiles> blocks_percentiles;

protected:
void MempoolTransactionsRemovedForBlock(const std::vector<RemovedMempoolTransactionInfo>& txs_removed_for_block, unsigned int /*unused*/) override;

public:
BlockFees(){};

ForecastResult EstimateFee(const unsigned int& targetBlocks) override;
unsigned int MaxTarget() override
{
return BLOCK_MAX_CONF_TARGET;
}
};
#endif // BITCOIN_POLICY_BLOCK_FEES_H

0 comments on commit a15cffd

Please sign in to comment.