diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 5825efdf82ea1c..688f58377e69a7 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -250,6 +250,8 @@ static const CRPCConvertParam vRPCConvertParams[] = { "estimatesmartfee", 0, "conf_target" }, { "estimaterawfee", 0, "conf_target" }, { "estimaterawfee", 1, "threshold" }, + { "estimatefeewithmempool", 0, "conf_target" }, + { "estimatefeewithmempool", 1, "force" }, { "prioritisetransaction", 1, "dummy" }, { "prioritisetransaction", 2, "fee_delta" }, { "setban", 2, "bantime" }, diff --git a/src/rpc/fees.cpp b/src/rpc/fees.cpp index 57ba486ed9035e..6bb4706a69840e 100644 --- a/src/rpc/fees.cpp +++ b/src/rpc/fees.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -14,6 +15,7 @@ #include #include #include +#include #include #include @@ -99,6 +101,58 @@ static RPCHelpMan estimatesmartfee() }; } +static RPCHelpMan estimatefeewithmempool() +{ + return RPCHelpMan{ + "estimatefeewithmempool", + "\nEstimates the approximate fee per kilobyte needed for a transaction to begin\n" + "confirmation within conf_target blocks if possible Uses virtual transaction size as defined\n" + "in BIP 141 (witness data is discounted). By default caches values for 30 seconds to avoid\n" + "repeatedly running expensive block-building algorithm.\n", + { + {"conf_target", RPCArg::Type::NUM, RPCArg::Optional::NO, "Confirmation target in blocks"}, + {"force", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED, "Force run block-building algorithm, bypassing any cached values."}, + }, + RPCResult{ + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::NUM, "feerate", /*optional=*/true, "estimate fee rate in " + CURRENCY_UNIT + "/kvB (only present if no errors were encountered)"}, + {RPCResult::Type::ARR, "errors", /*optional=*/true, "Errors encountered during processing (if there are any)", + {{RPCResult::Type::STR, "", "error"},} + }, + }}, + RPCExamples{HelpExampleCli("estimatesfeewithmempool", "2") + HelpExampleRpc("estimatesfeewithmempool", "2")}, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { + MemPoolPolicyEstimator& fee_estimator = EnsureAnyMemPoolFeeEstimator(request.context); + const NodeContext& node = EnsureAnyNodeContext(request.context); + CTxMemPool& mempool = EnsureMemPool(node); + ChainstateManager& chainman = EnsureChainman(node); + Chainstate& chainstate = chainman.ActiveChainstate(); + const unsigned int conf_target = request.params[0].getInt(); + bool force{false}; + if (!request.params[1].isNull()) force = request.params[1].get_bool(); + CFeeRate feeRate; + std::string err_message; + { + LOCK2(cs_main, mempool.cs); + feeRate = fee_estimator.EstimateFeeWithMemPool(chainstate, mempool, conf_target, force, err_message); + } + UniValue result(UniValue::VOBJ); + UniValue errors(UniValue::VARR); + if (feeRate != CFeeRate(0)) { + CFeeRate min_mempool_feerate{mempool.GetMinFee()}; + CFeeRate min_relay_feerate{mempool.m_min_relay_feerate}; + feeRate = std::max({feeRate, min_mempool_feerate, min_relay_feerate}); + result.pushKV("feerate", ValueFromAmount(feeRate.GetFeePerK())); + } else { + errors.push_back(err_message.c_str()); + result.pushKV("errors", errors); + } + return result; + }, + }; +} + static RPCHelpMan estimaterawfee() { return RPCHelpMan{"estimaterawfee", @@ -222,6 +276,7 @@ void RegisterFeeRPCCommands(CRPCTable& t) { static const CRPCCommand commands[]{ {"util", &estimatesmartfee}, + {"util", &estimatefeewithmempool}, {"hidden", &estimaterawfee}, }; for (const auto& c : commands) { diff --git a/src/rpc/server_util.cpp b/src/rpc/server_util.cpp index efd4a43c288c2e..9bbcb11f3239d7 100644 --- a/src/rpc/server_util.cpp +++ b/src/rpc/server_util.cpp @@ -93,6 +93,19 @@ CBlockPolicyEstimator& EnsureAnyFeeEstimator(const std::any& context) return EnsureFeeEstimator(EnsureAnyNodeContext(context)); } +MemPoolPolicyEstimator& EnsureMemPoolFeeEstimator(const NodeContext& node) +{ + if (!node.mempool_fee_estimator) { + throw JSONRPCError(RPC_INTERNAL_ERROR, "MemPool based Fee estimation disabled"); + } + return *node.mempool_fee_estimator; +} + +MemPoolPolicyEstimator& EnsureAnyMemPoolFeeEstimator(const std::any& context) +{ + return EnsureMemPoolFeeEstimator(EnsureAnyNodeContext(context)); +} + CConnman& EnsureConnman(const NodeContext& node) { if (!node.connman) { diff --git a/src/rpc/server_util.h b/src/rpc/server_util.h index a4a53166b4b61f..dc5d234ea9d65b 100644 --- a/src/rpc/server_util.h +++ b/src/rpc/server_util.h @@ -11,6 +11,7 @@ class AddrMan; class ArgsManager; class CBlockPolicyEstimator; class CConnman; +class MemPoolPolicyEstimator; class CTxMemPool; class ChainstateManager; class PeerManager; @@ -30,6 +31,8 @@ ChainstateManager& EnsureChainman(const node::NodeContext& node); ChainstateManager& EnsureAnyChainman(const std::any& context); CBlockPolicyEstimator& EnsureFeeEstimator(const node::NodeContext& node); CBlockPolicyEstimator& EnsureAnyFeeEstimator(const std::any& context); +MemPoolPolicyEstimator& EnsureMemPoolFeeEstimator(const node::NodeContext& node); +MemPoolPolicyEstimator& EnsureAnyMemPoolFeeEstimator(const std::any& context); CConnman& EnsureConnman(const node::NodeContext& node); PeerManager& EnsurePeerman(const node::NodeContext& node); AddrMan& EnsureAddrman(const node::NodeContext& node);