mirror of
https://github.com/Retropex/bitcoin.git
synced 2025-08-05 22:44:50 +02:00
rpc: Move mempool RPCs to new file
Can be reviewed with: --color-moved=dimmed-zebra --color-moved-ws=ignore-all-space
This commit is contained in:
parent
a81717443f
commit
fa2a5f301a
@ -209,6 +209,7 @@ BITCOIN_CORE_H = \
|
|||||||
reverse_iterator.h \
|
reverse_iterator.h \
|
||||||
rpc/blockchain.h \
|
rpc/blockchain.h \
|
||||||
rpc/client.h \
|
rpc/client.h \
|
||||||
|
rpc/mempool.h \
|
||||||
rpc/mining.h \
|
rpc/mining.h \
|
||||||
rpc/protocol.h \
|
rpc/protocol.h \
|
||||||
rpc/rawtransaction_util.h \
|
rpc/rawtransaction_util.h \
|
||||||
@ -370,6 +371,7 @@ libbitcoin_node_a_SOURCES = \
|
|||||||
pow.cpp \
|
pow.cpp \
|
||||||
rest.cpp \
|
rest.cpp \
|
||||||
rpc/blockchain.cpp \
|
rpc/blockchain.cpp \
|
||||||
|
rpc/mempool.cpp \
|
||||||
rpc/mining.cpp \
|
rpc/mining.cpp \
|
||||||
rpc/misc.cpp \
|
rpc/misc.cpp \
|
||||||
rpc/net.cpp \
|
rpc/net.cpp \
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
#include <bench/bench.h>
|
#include <bench/bench.h>
|
||||||
#include <rpc/blockchain.h>
|
#include <rpc/mempool.h>
|
||||||
#include <txmempool.h>
|
#include <txmempool.h>
|
||||||
|
|
||||||
#include <univalue.h>
|
#include <univalue.h>
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include <primitives/block.h>
|
#include <primitives/block.h>
|
||||||
#include <primitives/transaction.h>
|
#include <primitives/transaction.h>
|
||||||
#include <rpc/blockchain.h>
|
#include <rpc/blockchain.h>
|
||||||
|
#include <rpc/mempool.h>
|
||||||
#include <rpc/protocol.h>
|
#include <rpc/protocol.h>
|
||||||
#include <rpc/server.h>
|
#include <rpc/server.h>
|
||||||
#include <rpc/server_util.h>
|
#include <rpc/server_util.h>
|
||||||
|
@ -26,10 +26,6 @@
|
|||||||
#include <node/coinstats.h>
|
#include <node/coinstats.h>
|
||||||
#include <node/context.h>
|
#include <node/context.h>
|
||||||
#include <node/utxo_snapshot.h>
|
#include <node/utxo_snapshot.h>
|
||||||
#include <policy/feerate.h>
|
|
||||||
#include <policy/fees.h>
|
|
||||||
#include <policy/policy.h>
|
|
||||||
#include <policy/rbf.h>
|
|
||||||
#include <primitives/transaction.h>
|
#include <primitives/transaction.h>
|
||||||
#include <rpc/server.h>
|
#include <rpc/server.h>
|
||||||
#include <rpc/server_util.h>
|
#include <rpc/server_util.h>
|
||||||
@ -40,8 +36,8 @@
|
|||||||
#include <txdb.h>
|
#include <txdb.h>
|
||||||
#include <txmempool.h>
|
#include <txmempool.h>
|
||||||
#include <undo.h>
|
#include <undo.h>
|
||||||
|
#include <univalue.h>
|
||||||
#include <util/strencodings.h>
|
#include <util/strencodings.h>
|
||||||
#include <util/string.h>
|
|
||||||
#include <util/translation.h>
|
#include <util/translation.h>
|
||||||
#include <validation.h>
|
#include <validation.h>
|
||||||
#include <validationinterface.h>
|
#include <validationinterface.h>
|
||||||
@ -50,8 +46,6 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include <univalue.h>
|
|
||||||
|
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
@ -426,366 +420,6 @@ static RPCHelpMan getdifficulty()
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::vector<RPCResult> MempoolEntryDescription() { return {
|
|
||||||
RPCResult{RPCResult::Type::NUM, "vsize", "virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted."},
|
|
||||||
RPCResult{RPCResult::Type::NUM, "weight", "transaction weight as defined in BIP 141."},
|
|
||||||
RPCResult{RPCResult::Type::STR_AMOUNT, "fee", /*optional=*/true,
|
|
||||||
"transaction fee, denominated in " + CURRENCY_UNIT + " (DEPRECATED, returned only if config option -deprecatedrpc=fees is passed)"},
|
|
||||||
RPCResult{RPCResult::Type::STR_AMOUNT, "modifiedfee", /*optional=*/true,
|
|
||||||
"transaction fee with fee deltas used for mining priority, denominated in " + CURRENCY_UNIT +
|
|
||||||
" (DEPRECATED, returned only if config option -deprecatedrpc=fees is passed)"},
|
|
||||||
RPCResult{RPCResult::Type::NUM_TIME, "time", "local time transaction entered pool in seconds since 1 Jan 1970 GMT"},
|
|
||||||
RPCResult{RPCResult::Type::NUM, "height", "block height when transaction entered pool"},
|
|
||||||
RPCResult{RPCResult::Type::NUM, "descendantcount", "number of in-mempool descendant transactions (including this one)"},
|
|
||||||
RPCResult{RPCResult::Type::NUM, "descendantsize", "virtual transaction size of in-mempool descendants (including this one)"},
|
|
||||||
RPCResult{RPCResult::Type::STR_AMOUNT, "descendantfees", /*optional=*/true,
|
|
||||||
"transaction fees of in-mempool descendants (including this one) with fee deltas used for mining priority, denominated in " +
|
|
||||||
CURRENCY_ATOM + "s (DEPRECATED, returned only if config option -deprecatedrpc=fees is passed)"},
|
|
||||||
RPCResult{RPCResult::Type::NUM, "ancestorcount", "number of in-mempool ancestor transactions (including this one)"},
|
|
||||||
RPCResult{RPCResult::Type::NUM, "ancestorsize", "virtual transaction size of in-mempool ancestors (including this one)"},
|
|
||||||
RPCResult{RPCResult::Type::STR_AMOUNT, "ancestorfees", /*optional=*/true,
|
|
||||||
"transaction fees of in-mempool ancestors (including this one) with fee deltas used for mining priority, denominated in " +
|
|
||||||
CURRENCY_ATOM + "s (DEPRECATED, returned only if config option -deprecatedrpc=fees is passed)"},
|
|
||||||
RPCResult{RPCResult::Type::STR_HEX, "wtxid", "hash of serialized transaction, including witness data"},
|
|
||||||
RPCResult{RPCResult::Type::OBJ, "fees", "",
|
|
||||||
{
|
|
||||||
RPCResult{RPCResult::Type::STR_AMOUNT, "base", "transaction fee, denominated in " + CURRENCY_UNIT},
|
|
||||||
RPCResult{RPCResult::Type::STR_AMOUNT, "modified", "transaction fee with fee deltas used for mining priority, denominated in " + CURRENCY_UNIT},
|
|
||||||
RPCResult{RPCResult::Type::STR_AMOUNT, "ancestor", "transaction fees of in-mempool ancestors (including this one) with fee deltas used for mining priority, denominated in " + CURRENCY_UNIT},
|
|
||||||
RPCResult{RPCResult::Type::STR_AMOUNT, "descendant", "transaction fees of in-mempool descendants (including this one) with fee deltas used for mining priority, denominated in " + CURRENCY_UNIT},
|
|
||||||
}},
|
|
||||||
RPCResult{RPCResult::Type::ARR, "depends", "unconfirmed transactions used as inputs for this transaction",
|
|
||||||
{RPCResult{RPCResult::Type::STR_HEX, "transactionid", "parent transaction id"}}},
|
|
||||||
RPCResult{RPCResult::Type::ARR, "spentby", "unconfirmed transactions spending outputs from this transaction",
|
|
||||||
{RPCResult{RPCResult::Type::STR_HEX, "transactionid", "child transaction id"}}},
|
|
||||||
RPCResult{RPCResult::Type::BOOL, "bip125-replaceable", "Whether this transaction could be replaced due to BIP125 (replace-by-fee)"},
|
|
||||||
RPCResult{RPCResult::Type::BOOL, "unbroadcast", "Whether this transaction is currently unbroadcast (initial broadcast not yet acknowledged by any peers)"},
|
|
||||||
};}
|
|
||||||
|
|
||||||
static void entryToJSON(const CTxMemPool& pool, UniValue& info, const CTxMemPoolEntry& e) EXCLUSIVE_LOCKS_REQUIRED(pool.cs)
|
|
||||||
{
|
|
||||||
AssertLockHeld(pool.cs);
|
|
||||||
|
|
||||||
info.pushKV("vsize", (int)e.GetTxSize());
|
|
||||||
info.pushKV("weight", (int)e.GetTxWeight());
|
|
||||||
// TODO: top-level fee fields are deprecated. deprecated_fee_fields_enabled blocks should be removed in v24
|
|
||||||
const bool deprecated_fee_fields_enabled{IsDeprecatedRPCEnabled("fees")};
|
|
||||||
if (deprecated_fee_fields_enabled) {
|
|
||||||
info.pushKV("fee", ValueFromAmount(e.GetFee()));
|
|
||||||
info.pushKV("modifiedfee", ValueFromAmount(e.GetModifiedFee()));
|
|
||||||
}
|
|
||||||
info.pushKV("time", count_seconds(e.GetTime()));
|
|
||||||
info.pushKV("height", (int)e.GetHeight());
|
|
||||||
info.pushKV("descendantcount", e.GetCountWithDescendants());
|
|
||||||
info.pushKV("descendantsize", e.GetSizeWithDescendants());
|
|
||||||
if (deprecated_fee_fields_enabled) {
|
|
||||||
info.pushKV("descendantfees", e.GetModFeesWithDescendants());
|
|
||||||
}
|
|
||||||
info.pushKV("ancestorcount", e.GetCountWithAncestors());
|
|
||||||
info.pushKV("ancestorsize", e.GetSizeWithAncestors());
|
|
||||||
if (deprecated_fee_fields_enabled) {
|
|
||||||
info.pushKV("ancestorfees", e.GetModFeesWithAncestors());
|
|
||||||
}
|
|
||||||
info.pushKV("wtxid", pool.vTxHashes[e.vTxHashesIdx].first.ToString());
|
|
||||||
|
|
||||||
UniValue fees(UniValue::VOBJ);
|
|
||||||
fees.pushKV("base", ValueFromAmount(e.GetFee()));
|
|
||||||
fees.pushKV("modified", ValueFromAmount(e.GetModifiedFee()));
|
|
||||||
fees.pushKV("ancestor", ValueFromAmount(e.GetModFeesWithAncestors()));
|
|
||||||
fees.pushKV("descendant", ValueFromAmount(e.GetModFeesWithDescendants()));
|
|
||||||
info.pushKV("fees", fees);
|
|
||||||
|
|
||||||
const CTransaction& tx = e.GetTx();
|
|
||||||
std::set<std::string> setDepends;
|
|
||||||
for (const CTxIn& txin : tx.vin)
|
|
||||||
{
|
|
||||||
if (pool.exists(GenTxid::Txid(txin.prevout.hash)))
|
|
||||||
setDepends.insert(txin.prevout.hash.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
UniValue depends(UniValue::VARR);
|
|
||||||
for (const std::string& dep : setDepends)
|
|
||||||
{
|
|
||||||
depends.push_back(dep);
|
|
||||||
}
|
|
||||||
|
|
||||||
info.pushKV("depends", depends);
|
|
||||||
|
|
||||||
UniValue spent(UniValue::VARR);
|
|
||||||
const CTxMemPool::txiter& it = pool.mapTx.find(tx.GetHash());
|
|
||||||
const CTxMemPoolEntry::Children& children = it->GetMemPoolChildrenConst();
|
|
||||||
for (const CTxMemPoolEntry& child : children) {
|
|
||||||
spent.push_back(child.GetTx().GetHash().ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
info.pushKV("spentby", spent);
|
|
||||||
|
|
||||||
// Add opt-in RBF status
|
|
||||||
bool rbfStatus = false;
|
|
||||||
RBFTransactionState rbfState = IsRBFOptIn(tx, pool);
|
|
||||||
if (rbfState == RBFTransactionState::UNKNOWN) {
|
|
||||||
throw JSONRPCError(RPC_MISC_ERROR, "Transaction is not in mempool");
|
|
||||||
} else if (rbfState == RBFTransactionState::REPLACEABLE_BIP125) {
|
|
||||||
rbfStatus = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
info.pushKV("bip125-replaceable", rbfStatus);
|
|
||||||
info.pushKV("unbroadcast", pool.IsUnbroadcastTx(tx.GetHash()));
|
|
||||||
}
|
|
||||||
|
|
||||||
UniValue MempoolToJSON(const CTxMemPool& pool, bool verbose, bool include_mempool_sequence)
|
|
||||||
{
|
|
||||||
if (verbose) {
|
|
||||||
if (include_mempool_sequence) {
|
|
||||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Verbose results cannot contain mempool sequence values.");
|
|
||||||
}
|
|
||||||
LOCK(pool.cs);
|
|
||||||
UniValue o(UniValue::VOBJ);
|
|
||||||
for (const CTxMemPoolEntry& e : pool.mapTx) {
|
|
||||||
const uint256& hash = e.GetTx().GetHash();
|
|
||||||
UniValue info(UniValue::VOBJ);
|
|
||||||
entryToJSON(pool, info, e);
|
|
||||||
// Mempool has unique entries so there is no advantage in using
|
|
||||||
// UniValue::pushKV, which checks if the key already exists in O(N).
|
|
||||||
// UniValue::__pushKV is used instead which currently is O(1).
|
|
||||||
o.__pushKV(hash.ToString(), info);
|
|
||||||
}
|
|
||||||
return o;
|
|
||||||
} else {
|
|
||||||
uint64_t mempool_sequence;
|
|
||||||
std::vector<uint256> vtxid;
|
|
||||||
{
|
|
||||||
LOCK(pool.cs);
|
|
||||||
pool.queryHashes(vtxid);
|
|
||||||
mempool_sequence = pool.GetSequence();
|
|
||||||
}
|
|
||||||
UniValue a(UniValue::VARR);
|
|
||||||
for (const uint256& hash : vtxid)
|
|
||||||
a.push_back(hash.ToString());
|
|
||||||
|
|
||||||
if (!include_mempool_sequence) {
|
|
||||||
return a;
|
|
||||||
} else {
|
|
||||||
UniValue o(UniValue::VOBJ);
|
|
||||||
o.pushKV("txids", a);
|
|
||||||
o.pushKV("mempool_sequence", mempool_sequence);
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static RPCHelpMan getrawmempool()
|
|
||||||
{
|
|
||||||
return RPCHelpMan{"getrawmempool",
|
|
||||||
"\nReturns all transaction ids in memory pool as a json array of string transaction ids.\n"
|
|
||||||
"\nHint: use getmempoolentry to fetch a specific transaction from the mempool.\n",
|
|
||||||
{
|
|
||||||
{"verbose", RPCArg::Type::BOOL, RPCArg::Default{false}, "True for a json object, false for array of transaction ids"},
|
|
||||||
{"mempool_sequence", RPCArg::Type::BOOL, RPCArg::Default{false}, "If verbose=false, returns a json object with transaction list and mempool sequence number attached."},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
RPCResult{"for verbose = false",
|
|
||||||
RPCResult::Type::ARR, "", "",
|
|
||||||
{
|
|
||||||
{RPCResult::Type::STR_HEX, "", "The transaction id"},
|
|
||||||
}},
|
|
||||||
RPCResult{"for verbose = true",
|
|
||||||
RPCResult::Type::OBJ_DYN, "", "",
|
|
||||||
{
|
|
||||||
{RPCResult::Type::OBJ, "transactionid", "", MempoolEntryDescription()},
|
|
||||||
}},
|
|
||||||
RPCResult{"for verbose = false and mempool_sequence = true",
|
|
||||||
RPCResult::Type::OBJ, "", "",
|
|
||||||
{
|
|
||||||
{RPCResult::Type::ARR, "txids", "",
|
|
||||||
{
|
|
||||||
{RPCResult::Type::STR_HEX, "", "The transaction id"},
|
|
||||||
}},
|
|
||||||
{RPCResult::Type::NUM, "mempool_sequence", "The mempool sequence value."},
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
RPCExamples{
|
|
||||||
HelpExampleCli("getrawmempool", "true")
|
|
||||||
+ HelpExampleRpc("getrawmempool", "true")
|
|
||||||
},
|
|
||||||
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
|
||||||
{
|
|
||||||
bool fVerbose = false;
|
|
||||||
if (!request.params[0].isNull())
|
|
||||||
fVerbose = request.params[0].get_bool();
|
|
||||||
|
|
||||||
bool include_mempool_sequence = false;
|
|
||||||
if (!request.params[1].isNull()) {
|
|
||||||
include_mempool_sequence = request.params[1].get_bool();
|
|
||||||
}
|
|
||||||
|
|
||||||
return MempoolToJSON(EnsureAnyMemPool(request.context), fVerbose, include_mempool_sequence);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static RPCHelpMan getmempoolancestors()
|
|
||||||
{
|
|
||||||
return RPCHelpMan{"getmempoolancestors",
|
|
||||||
"\nIf txid is in the mempool, returns all in-mempool ancestors.\n",
|
|
||||||
{
|
|
||||||
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id (must be in mempool)"},
|
|
||||||
{"verbose", RPCArg::Type::BOOL, RPCArg::Default{false}, "True for a json object, false for array of transaction ids"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
RPCResult{"for verbose = false",
|
|
||||||
RPCResult::Type::ARR, "", "",
|
|
||||||
{{RPCResult::Type::STR_HEX, "", "The transaction id of an in-mempool ancestor transaction"}}},
|
|
||||||
RPCResult{"for verbose = true",
|
|
||||||
RPCResult::Type::OBJ_DYN, "", "",
|
|
||||||
{
|
|
||||||
{RPCResult::Type::OBJ, "transactionid", "", MempoolEntryDescription()},
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
RPCExamples{
|
|
||||||
HelpExampleCli("getmempoolancestors", "\"mytxid\"")
|
|
||||||
+ HelpExampleRpc("getmempoolancestors", "\"mytxid\"")
|
|
||||||
},
|
|
||||||
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
|
||||||
{
|
|
||||||
bool fVerbose = false;
|
|
||||||
if (!request.params[1].isNull())
|
|
||||||
fVerbose = request.params[1].get_bool();
|
|
||||||
|
|
||||||
uint256 hash = ParseHashV(request.params[0], "parameter 1");
|
|
||||||
|
|
||||||
const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
|
|
||||||
LOCK(mempool.cs);
|
|
||||||
|
|
||||||
CTxMemPool::txiter it = mempool.mapTx.find(hash);
|
|
||||||
if (it == mempool.mapTx.end()) {
|
|
||||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool");
|
|
||||||
}
|
|
||||||
|
|
||||||
CTxMemPool::setEntries setAncestors;
|
|
||||||
uint64_t noLimit = std::numeric_limits<uint64_t>::max();
|
|
||||||
std::string dummy;
|
|
||||||
mempool.CalculateMemPoolAncestors(*it, setAncestors, noLimit, noLimit, noLimit, noLimit, dummy, false);
|
|
||||||
|
|
||||||
if (!fVerbose) {
|
|
||||||
UniValue o(UniValue::VARR);
|
|
||||||
for (CTxMemPool::txiter ancestorIt : setAncestors) {
|
|
||||||
o.push_back(ancestorIt->GetTx().GetHash().ToString());
|
|
||||||
}
|
|
||||||
return o;
|
|
||||||
} else {
|
|
||||||
UniValue o(UniValue::VOBJ);
|
|
||||||
for (CTxMemPool::txiter ancestorIt : setAncestors) {
|
|
||||||
const CTxMemPoolEntry &e = *ancestorIt;
|
|
||||||
const uint256& _hash = e.GetTx().GetHash();
|
|
||||||
UniValue info(UniValue::VOBJ);
|
|
||||||
entryToJSON(mempool, info, e);
|
|
||||||
o.pushKV(_hash.ToString(), info);
|
|
||||||
}
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static RPCHelpMan getmempooldescendants()
|
|
||||||
{
|
|
||||||
return RPCHelpMan{"getmempooldescendants",
|
|
||||||
"\nIf txid is in the mempool, returns all in-mempool descendants.\n",
|
|
||||||
{
|
|
||||||
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id (must be in mempool)"},
|
|
||||||
{"verbose", RPCArg::Type::BOOL, RPCArg::Default{false}, "True for a json object, false for array of transaction ids"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
RPCResult{"for verbose = false",
|
|
||||||
RPCResult::Type::ARR, "", "",
|
|
||||||
{{RPCResult::Type::STR_HEX, "", "The transaction id of an in-mempool descendant transaction"}}},
|
|
||||||
RPCResult{"for verbose = true",
|
|
||||||
RPCResult::Type::OBJ_DYN, "", "",
|
|
||||||
{
|
|
||||||
{RPCResult::Type::OBJ, "transactionid", "", MempoolEntryDescription()},
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
RPCExamples{
|
|
||||||
HelpExampleCli("getmempooldescendants", "\"mytxid\"")
|
|
||||||
+ HelpExampleRpc("getmempooldescendants", "\"mytxid\"")
|
|
||||||
},
|
|
||||||
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
|
||||||
{
|
|
||||||
bool fVerbose = false;
|
|
||||||
if (!request.params[1].isNull())
|
|
||||||
fVerbose = request.params[1].get_bool();
|
|
||||||
|
|
||||||
uint256 hash = ParseHashV(request.params[0], "parameter 1");
|
|
||||||
|
|
||||||
const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
|
|
||||||
LOCK(mempool.cs);
|
|
||||||
|
|
||||||
CTxMemPool::txiter it = mempool.mapTx.find(hash);
|
|
||||||
if (it == mempool.mapTx.end()) {
|
|
||||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool");
|
|
||||||
}
|
|
||||||
|
|
||||||
CTxMemPool::setEntries setDescendants;
|
|
||||||
mempool.CalculateDescendants(it, setDescendants);
|
|
||||||
// CTxMemPool::CalculateDescendants will include the given tx
|
|
||||||
setDescendants.erase(it);
|
|
||||||
|
|
||||||
if (!fVerbose) {
|
|
||||||
UniValue o(UniValue::VARR);
|
|
||||||
for (CTxMemPool::txiter descendantIt : setDescendants) {
|
|
||||||
o.push_back(descendantIt->GetTx().GetHash().ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
return o;
|
|
||||||
} else {
|
|
||||||
UniValue o(UniValue::VOBJ);
|
|
||||||
for (CTxMemPool::txiter descendantIt : setDescendants) {
|
|
||||||
const CTxMemPoolEntry &e = *descendantIt;
|
|
||||||
const uint256& _hash = e.GetTx().GetHash();
|
|
||||||
UniValue info(UniValue::VOBJ);
|
|
||||||
entryToJSON(mempool, info, e);
|
|
||||||
o.pushKV(_hash.ToString(), info);
|
|
||||||
}
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static RPCHelpMan getmempoolentry()
|
|
||||||
{
|
|
||||||
return RPCHelpMan{"getmempoolentry",
|
|
||||||
"\nReturns mempool data for given transaction\n",
|
|
||||||
{
|
|
||||||
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id (must be in mempool)"},
|
|
||||||
},
|
|
||||||
RPCResult{
|
|
||||||
RPCResult::Type::OBJ, "", "", MempoolEntryDescription()},
|
|
||||||
RPCExamples{
|
|
||||||
HelpExampleCli("getmempoolentry", "\"mytxid\"")
|
|
||||||
+ HelpExampleRpc("getmempoolentry", "\"mytxid\"")
|
|
||||||
},
|
|
||||||
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
|
||||||
{
|
|
||||||
uint256 hash = ParseHashV(request.params[0], "parameter 1");
|
|
||||||
|
|
||||||
const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
|
|
||||||
LOCK(mempool.cs);
|
|
||||||
|
|
||||||
CTxMemPool::txiter it = mempool.mapTx.find(hash);
|
|
||||||
if (it == mempool.mapTx.end()) {
|
|
||||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool");
|
|
||||||
}
|
|
||||||
|
|
||||||
const CTxMemPoolEntry &e = *it;
|
|
||||||
UniValue info(UniValue::VOBJ);
|
|
||||||
entryToJSON(mempool, info, e);
|
|
||||||
return info;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static RPCHelpMan getblockfrompeer()
|
static RPCHelpMan getblockfrompeer()
|
||||||
{
|
{
|
||||||
return RPCHelpMan{
|
return RPCHelpMan{
|
||||||
@ -1809,53 +1443,6 @@ static RPCHelpMan getchaintips()
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
UniValue MempoolInfoToJSON(const CTxMemPool& pool)
|
|
||||||
{
|
|
||||||
// Make sure this call is atomic in the pool.
|
|
||||||
LOCK(pool.cs);
|
|
||||||
UniValue ret(UniValue::VOBJ);
|
|
||||||
ret.pushKV("loaded", pool.IsLoaded());
|
|
||||||
ret.pushKV("size", (int64_t)pool.size());
|
|
||||||
ret.pushKV("bytes", (int64_t)pool.GetTotalTxSize());
|
|
||||||
ret.pushKV("usage", (int64_t)pool.DynamicMemoryUsage());
|
|
||||||
ret.pushKV("total_fee", ValueFromAmount(pool.GetTotalFee()));
|
|
||||||
size_t maxmempool = gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
|
|
||||||
ret.pushKV("maxmempool", (int64_t) maxmempool);
|
|
||||||
ret.pushKV("mempoolminfee", ValueFromAmount(std::max(pool.GetMinFee(maxmempool), ::minRelayTxFee).GetFeePerK()));
|
|
||||||
ret.pushKV("minrelaytxfee", ValueFromAmount(::minRelayTxFee.GetFeePerK()));
|
|
||||||
ret.pushKV("unbroadcastcount", uint64_t{pool.GetUnbroadcastTxs().size()});
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static RPCHelpMan getmempoolinfo()
|
|
||||||
{
|
|
||||||
return RPCHelpMan{"getmempoolinfo",
|
|
||||||
"\nReturns details on the active state of the TX memory pool.\n",
|
|
||||||
{},
|
|
||||||
RPCResult{
|
|
||||||
RPCResult::Type::OBJ, "", "",
|
|
||||||
{
|
|
||||||
{RPCResult::Type::BOOL, "loaded", "True if the mempool is fully loaded"},
|
|
||||||
{RPCResult::Type::NUM, "size", "Current tx count"},
|
|
||||||
{RPCResult::Type::NUM, "bytes", "Sum of all virtual transaction sizes as defined in BIP 141. Differs from actual serialized size because witness data is discounted"},
|
|
||||||
{RPCResult::Type::NUM, "usage", "Total memory usage for the mempool"},
|
|
||||||
{RPCResult::Type::STR_AMOUNT, "total_fee", "Total fees for the mempool in " + CURRENCY_UNIT + ", ignoring modified fees through prioritisetransaction"},
|
|
||||||
{RPCResult::Type::NUM, "maxmempool", "Maximum memory usage for the mempool"},
|
|
||||||
{RPCResult::Type::STR_AMOUNT, "mempoolminfee", "Minimum fee rate in " + CURRENCY_UNIT + "/kvB for tx to be accepted. Is the maximum of minrelaytxfee and minimum mempool fee"},
|
|
||||||
{RPCResult::Type::STR_AMOUNT, "minrelaytxfee", "Current minimum relay fee for transactions"},
|
|
||||||
{RPCResult::Type::NUM, "unbroadcastcount", "Current number of transactions that haven't passed initial broadcast yet"}
|
|
||||||
}},
|
|
||||||
RPCExamples{
|
|
||||||
HelpExampleCli("getmempoolinfo", "")
|
|
||||||
+ HelpExampleRpc("getmempoolinfo", "")
|
|
||||||
},
|
|
||||||
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
|
||||||
{
|
|
||||||
return MempoolInfoToJSON(EnsureAnyMemPool(request.context));
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static RPCHelpMan preciousblock()
|
static RPCHelpMan preciousblock()
|
||||||
{
|
{
|
||||||
return RPCHelpMan{"preciousblock",
|
return RPCHelpMan{"preciousblock",
|
||||||
@ -2352,41 +1939,6 @@ static RPCHelpMan getblockstats()
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static RPCHelpMan savemempool()
|
|
||||||
{
|
|
||||||
return RPCHelpMan{"savemempool",
|
|
||||||
"\nDumps the mempool to disk. It will fail until the previous dump is fully loaded.\n",
|
|
||||||
{},
|
|
||||||
RPCResult{
|
|
||||||
RPCResult::Type::OBJ, "", "",
|
|
||||||
{
|
|
||||||
{RPCResult::Type::STR, "filename", "the directory and file where the mempool was saved"},
|
|
||||||
}},
|
|
||||||
RPCExamples{
|
|
||||||
HelpExampleCli("savemempool", "")
|
|
||||||
+ HelpExampleRpc("savemempool", "")
|
|
||||||
},
|
|
||||||
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
|
||||||
{
|
|
||||||
const ArgsManager& args{EnsureAnyArgsman(request.context)};
|
|
||||||
const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
|
|
||||||
|
|
||||||
if (!mempool.IsLoaded()) {
|
|
||||||
throw JSONRPCError(RPC_MISC_ERROR, "The mempool was not loaded yet");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!DumpMempool(mempool)) {
|
|
||||||
throw JSONRPCError(RPC_MISC_ERROR, "Unable to dump mempool to disk");
|
|
||||||
}
|
|
||||||
|
|
||||||
UniValue ret(UniValue::VOBJ);
|
|
||||||
ret.pushKV("filename", fs::path((args.GetDataDirNet() / "mempool.dat")).u8string());
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
//! Search for a given set of pubkey scripts
|
//! Search for a given set of pubkey scripts
|
||||||
bool FindScriptPubKey(std::atomic<int>& scan_progress, const std::atomic<bool>& should_abort, int64_t& count, CCoinsViewCursor* cursor, const std::set<CScript>& needles, std::map<COutPoint, Coin>& out_results, std::function<void()>& interruption_point)
|
bool FindScriptPubKey(std::atomic<int>& scan_progress, const std::atomic<bool>& should_abort, int64_t& count, CCoinsViewCursor* cursor, const std::set<CScript>& needles, std::map<COutPoint, Coin>& out_results, std::function<void()>& interruption_point)
|
||||||
@ -2825,6 +2377,14 @@ UniValue CreateUTXOSnapshot(
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
RPCHelpMan getmempoolancestors();
|
||||||
|
RPCHelpMan getmempooldescendants();
|
||||||
|
RPCHelpMan getmempoolentry();
|
||||||
|
RPCHelpMan getmempoolinfo();
|
||||||
|
RPCHelpMan getrawmempool();
|
||||||
|
RPCHelpMan savemempool();
|
||||||
|
|
||||||
void RegisterBlockchainRPCCommands(CRPCTable &t)
|
void RegisterBlockchainRPCCommands(CRPCTable &t)
|
||||||
{
|
{
|
||||||
// clang-format off
|
// clang-format off
|
||||||
|
@ -20,7 +20,6 @@ extern RecursiveMutex cs_main;
|
|||||||
class CBlock;
|
class CBlock;
|
||||||
class CBlockIndex;
|
class CBlockIndex;
|
||||||
class CChainState;
|
class CChainState;
|
||||||
class CTxMemPool;
|
|
||||||
class UniValue;
|
class UniValue;
|
||||||
namespace node {
|
namespace node {
|
||||||
struct NodeContext;
|
struct NodeContext;
|
||||||
@ -42,12 +41,6 @@ void RPCNotifyBlockChange(const CBlockIndex*);
|
|||||||
/** Block description to JSON */
|
/** Block description to JSON */
|
||||||
UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIndex* blockindex, TxVerbosity verbosity) LOCKS_EXCLUDED(cs_main);
|
UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIndex* blockindex, TxVerbosity verbosity) LOCKS_EXCLUDED(cs_main);
|
||||||
|
|
||||||
/** Mempool information to JSON */
|
|
||||||
UniValue MempoolInfoToJSON(const CTxMemPool& pool);
|
|
||||||
|
|
||||||
/** Mempool to JSON */
|
|
||||||
UniValue MempoolToJSON(const CTxMemPool& pool, bool verbose = false, bool include_mempool_sequence = false);
|
|
||||||
|
|
||||||
/** Block header to JSON */
|
/** Block header to JSON */
|
||||||
UniValue blockheaderToJSON(const CBlockIndex* tip, const CBlockIndex* blockindex) LOCKS_EXCLUDED(cs_main);
|
UniValue blockheaderToJSON(const CBlockIndex* tip, const CBlockIndex* blockindex) LOCKS_EXCLUDED(cs_main);
|
||||||
|
|
||||||
|
459
src/rpc/mempool.cpp
Normal file
459
src/rpc/mempool.cpp
Normal file
@ -0,0 +1,459 @@
|
|||||||
|
// Copyright (c) 2010 Satoshi Nakamoto
|
||||||
|
// Copyright (c) 2009-2022 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 <rpc/blockchain.h>
|
||||||
|
|
||||||
|
#include <core_io.h>
|
||||||
|
#include <fs.h>
|
||||||
|
#include <policy/rbf.h>
|
||||||
|
#include <primitives/transaction.h>
|
||||||
|
#include <rpc/server.h>
|
||||||
|
#include <rpc/server_util.h>
|
||||||
|
#include <rpc/util.h>
|
||||||
|
#include <txmempool.h>
|
||||||
|
#include <univalue.h>
|
||||||
|
#include <validation.h>
|
||||||
|
|
||||||
|
static std::vector<RPCResult> MempoolEntryDescription() { return {
|
||||||
|
RPCResult{RPCResult::Type::NUM, "vsize", "virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted."},
|
||||||
|
RPCResult{RPCResult::Type::NUM, "weight", "transaction weight as defined in BIP 141."},
|
||||||
|
RPCResult{RPCResult::Type::STR_AMOUNT, "fee", /*optional=*/true,
|
||||||
|
"transaction fee, denominated in " + CURRENCY_UNIT + " (DEPRECATED, returned only if config option -deprecatedrpc=fees is passed)"},
|
||||||
|
RPCResult{RPCResult::Type::STR_AMOUNT, "modifiedfee", /*optional=*/true,
|
||||||
|
"transaction fee with fee deltas used for mining priority, denominated in " + CURRENCY_UNIT +
|
||||||
|
" (DEPRECATED, returned only if config option -deprecatedrpc=fees is passed)"},
|
||||||
|
RPCResult{RPCResult::Type::NUM_TIME, "time", "local time transaction entered pool in seconds since 1 Jan 1970 GMT"},
|
||||||
|
RPCResult{RPCResult::Type::NUM, "height", "block height when transaction entered pool"},
|
||||||
|
RPCResult{RPCResult::Type::NUM, "descendantcount", "number of in-mempool descendant transactions (including this one)"},
|
||||||
|
RPCResult{RPCResult::Type::NUM, "descendantsize", "virtual transaction size of in-mempool descendants (including this one)"},
|
||||||
|
RPCResult{RPCResult::Type::STR_AMOUNT, "descendantfees", /*optional=*/true,
|
||||||
|
"transaction fees of in-mempool descendants (including this one) with fee deltas used for mining priority, denominated in " +
|
||||||
|
CURRENCY_ATOM + "s (DEPRECATED, returned only if config option -deprecatedrpc=fees is passed)"},
|
||||||
|
RPCResult{RPCResult::Type::NUM, "ancestorcount", "number of in-mempool ancestor transactions (including this one)"},
|
||||||
|
RPCResult{RPCResult::Type::NUM, "ancestorsize", "virtual transaction size of in-mempool ancestors (including this one)"},
|
||||||
|
RPCResult{RPCResult::Type::STR_AMOUNT, "ancestorfees", /*optional=*/true,
|
||||||
|
"transaction fees of in-mempool ancestors (including this one) with fee deltas used for mining priority, denominated in " +
|
||||||
|
CURRENCY_ATOM + "s (DEPRECATED, returned only if config option -deprecatedrpc=fees is passed)"},
|
||||||
|
RPCResult{RPCResult::Type::STR_HEX, "wtxid", "hash of serialized transaction, including witness data"},
|
||||||
|
RPCResult{RPCResult::Type::OBJ, "fees", "",
|
||||||
|
{
|
||||||
|
RPCResult{RPCResult::Type::STR_AMOUNT, "base", "transaction fee, denominated in " + CURRENCY_UNIT},
|
||||||
|
RPCResult{RPCResult::Type::STR_AMOUNT, "modified", "transaction fee with fee deltas used for mining priority, denominated in " + CURRENCY_UNIT},
|
||||||
|
RPCResult{RPCResult::Type::STR_AMOUNT, "ancestor", "transaction fees of in-mempool ancestors (including this one) with fee deltas used for mining priority, denominated in " + CURRENCY_UNIT},
|
||||||
|
RPCResult{RPCResult::Type::STR_AMOUNT, "descendant", "transaction fees of in-mempool descendants (including this one) with fee deltas used for mining priority, denominated in " + CURRENCY_UNIT},
|
||||||
|
}},
|
||||||
|
RPCResult{RPCResult::Type::ARR, "depends", "unconfirmed transactions used as inputs for this transaction",
|
||||||
|
{RPCResult{RPCResult::Type::STR_HEX, "transactionid", "parent transaction id"}}},
|
||||||
|
RPCResult{RPCResult::Type::ARR, "spentby", "unconfirmed transactions spending outputs from this transaction",
|
||||||
|
{RPCResult{RPCResult::Type::STR_HEX, "transactionid", "child transaction id"}}},
|
||||||
|
RPCResult{RPCResult::Type::BOOL, "bip125-replaceable", "Whether this transaction could be replaced due to BIP125 (replace-by-fee)"},
|
||||||
|
RPCResult{RPCResult::Type::BOOL, "unbroadcast", "Whether this transaction is currently unbroadcast (initial broadcast not yet acknowledged by any peers)"},
|
||||||
|
};}
|
||||||
|
|
||||||
|
static void entryToJSON(const CTxMemPool& pool, UniValue& info, const CTxMemPoolEntry& e) EXCLUSIVE_LOCKS_REQUIRED(pool.cs)
|
||||||
|
{
|
||||||
|
AssertLockHeld(pool.cs);
|
||||||
|
|
||||||
|
info.pushKV("vsize", (int)e.GetTxSize());
|
||||||
|
info.pushKV("weight", (int)e.GetTxWeight());
|
||||||
|
// TODO: top-level fee fields are deprecated. deprecated_fee_fields_enabled blocks should be removed in v24
|
||||||
|
const bool deprecated_fee_fields_enabled{IsDeprecatedRPCEnabled("fees")};
|
||||||
|
if (deprecated_fee_fields_enabled) {
|
||||||
|
info.pushKV("fee", ValueFromAmount(e.GetFee()));
|
||||||
|
info.pushKV("modifiedfee", ValueFromAmount(e.GetModifiedFee()));
|
||||||
|
}
|
||||||
|
info.pushKV("time", count_seconds(e.GetTime()));
|
||||||
|
info.pushKV("height", (int)e.GetHeight());
|
||||||
|
info.pushKV("descendantcount", e.GetCountWithDescendants());
|
||||||
|
info.pushKV("descendantsize", e.GetSizeWithDescendants());
|
||||||
|
if (deprecated_fee_fields_enabled) {
|
||||||
|
info.pushKV("descendantfees", e.GetModFeesWithDescendants());
|
||||||
|
}
|
||||||
|
info.pushKV("ancestorcount", e.GetCountWithAncestors());
|
||||||
|
info.pushKV("ancestorsize", e.GetSizeWithAncestors());
|
||||||
|
if (deprecated_fee_fields_enabled) {
|
||||||
|
info.pushKV("ancestorfees", e.GetModFeesWithAncestors());
|
||||||
|
}
|
||||||
|
info.pushKV("wtxid", pool.vTxHashes[e.vTxHashesIdx].first.ToString());
|
||||||
|
|
||||||
|
UniValue fees(UniValue::VOBJ);
|
||||||
|
fees.pushKV("base", ValueFromAmount(e.GetFee()));
|
||||||
|
fees.pushKV("modified", ValueFromAmount(e.GetModifiedFee()));
|
||||||
|
fees.pushKV("ancestor", ValueFromAmount(e.GetModFeesWithAncestors()));
|
||||||
|
fees.pushKV("descendant", ValueFromAmount(e.GetModFeesWithDescendants()));
|
||||||
|
info.pushKV("fees", fees);
|
||||||
|
|
||||||
|
const CTransaction& tx = e.GetTx();
|
||||||
|
std::set<std::string> setDepends;
|
||||||
|
for (const CTxIn& txin : tx.vin)
|
||||||
|
{
|
||||||
|
if (pool.exists(GenTxid::Txid(txin.prevout.hash)))
|
||||||
|
setDepends.insert(txin.prevout.hash.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
UniValue depends(UniValue::VARR);
|
||||||
|
for (const std::string& dep : setDepends)
|
||||||
|
{
|
||||||
|
depends.push_back(dep);
|
||||||
|
}
|
||||||
|
|
||||||
|
info.pushKV("depends", depends);
|
||||||
|
|
||||||
|
UniValue spent(UniValue::VARR);
|
||||||
|
const CTxMemPool::txiter& it = pool.mapTx.find(tx.GetHash());
|
||||||
|
const CTxMemPoolEntry::Children& children = it->GetMemPoolChildrenConst();
|
||||||
|
for (const CTxMemPoolEntry& child : children) {
|
||||||
|
spent.push_back(child.GetTx().GetHash().ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
info.pushKV("spentby", spent);
|
||||||
|
|
||||||
|
// Add opt-in RBF status
|
||||||
|
bool rbfStatus = false;
|
||||||
|
RBFTransactionState rbfState = IsRBFOptIn(tx, pool);
|
||||||
|
if (rbfState == RBFTransactionState::UNKNOWN) {
|
||||||
|
throw JSONRPCError(RPC_MISC_ERROR, "Transaction is not in mempool");
|
||||||
|
} else if (rbfState == RBFTransactionState::REPLACEABLE_BIP125) {
|
||||||
|
rbfStatus = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
info.pushKV("bip125-replaceable", rbfStatus);
|
||||||
|
info.pushKV("unbroadcast", pool.IsUnbroadcastTx(tx.GetHash()));
|
||||||
|
}
|
||||||
|
|
||||||
|
UniValue MempoolToJSON(const CTxMemPool& pool, bool verbose, bool include_mempool_sequence)
|
||||||
|
{
|
||||||
|
if (verbose) {
|
||||||
|
if (include_mempool_sequence) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Verbose results cannot contain mempool sequence values.");
|
||||||
|
}
|
||||||
|
LOCK(pool.cs);
|
||||||
|
UniValue o(UniValue::VOBJ);
|
||||||
|
for (const CTxMemPoolEntry& e : pool.mapTx) {
|
||||||
|
const uint256& hash = e.GetTx().GetHash();
|
||||||
|
UniValue info(UniValue::VOBJ);
|
||||||
|
entryToJSON(pool, info, e);
|
||||||
|
// Mempool has unique entries so there is no advantage in using
|
||||||
|
// UniValue::pushKV, which checks if the key already exists in O(N).
|
||||||
|
// UniValue::__pushKV is used instead which currently is O(1).
|
||||||
|
o.__pushKV(hash.ToString(), info);
|
||||||
|
}
|
||||||
|
return o;
|
||||||
|
} else {
|
||||||
|
uint64_t mempool_sequence;
|
||||||
|
std::vector<uint256> vtxid;
|
||||||
|
{
|
||||||
|
LOCK(pool.cs);
|
||||||
|
pool.queryHashes(vtxid);
|
||||||
|
mempool_sequence = pool.GetSequence();
|
||||||
|
}
|
||||||
|
UniValue a(UniValue::VARR);
|
||||||
|
for (const uint256& hash : vtxid)
|
||||||
|
a.push_back(hash.ToString());
|
||||||
|
|
||||||
|
if (!include_mempool_sequence) {
|
||||||
|
return a;
|
||||||
|
} else {
|
||||||
|
UniValue o(UniValue::VOBJ);
|
||||||
|
o.pushKV("txids", a);
|
||||||
|
o.pushKV("mempool_sequence", mempool_sequence);
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RPCHelpMan getrawmempool()
|
||||||
|
{
|
||||||
|
return RPCHelpMan{"getrawmempool",
|
||||||
|
"\nReturns all transaction ids in memory pool as a json array of string transaction ids.\n"
|
||||||
|
"\nHint: use getmempoolentry to fetch a specific transaction from the mempool.\n",
|
||||||
|
{
|
||||||
|
{"verbose", RPCArg::Type::BOOL, RPCArg::Default{false}, "True for a json object, false for array of transaction ids"},
|
||||||
|
{"mempool_sequence", RPCArg::Type::BOOL, RPCArg::Default{false}, "If verbose=false, returns a json object with transaction list and mempool sequence number attached."},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
RPCResult{"for verbose = false",
|
||||||
|
RPCResult::Type::ARR, "", "",
|
||||||
|
{
|
||||||
|
{RPCResult::Type::STR_HEX, "", "The transaction id"},
|
||||||
|
}},
|
||||||
|
RPCResult{"for verbose = true",
|
||||||
|
RPCResult::Type::OBJ_DYN, "", "",
|
||||||
|
{
|
||||||
|
{RPCResult::Type::OBJ, "transactionid", "", MempoolEntryDescription()},
|
||||||
|
}},
|
||||||
|
RPCResult{"for verbose = false and mempool_sequence = true",
|
||||||
|
RPCResult::Type::OBJ, "", "",
|
||||||
|
{
|
||||||
|
{RPCResult::Type::ARR, "txids", "",
|
||||||
|
{
|
||||||
|
{RPCResult::Type::STR_HEX, "", "The transaction id"},
|
||||||
|
}},
|
||||||
|
{RPCResult::Type::NUM, "mempool_sequence", "The mempool sequence value."},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
RPCExamples{
|
||||||
|
HelpExampleCli("getrawmempool", "true")
|
||||||
|
+ HelpExampleRpc("getrawmempool", "true")
|
||||||
|
},
|
||||||
|
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
||||||
|
{
|
||||||
|
bool fVerbose = false;
|
||||||
|
if (!request.params[0].isNull())
|
||||||
|
fVerbose = request.params[0].get_bool();
|
||||||
|
|
||||||
|
bool include_mempool_sequence = false;
|
||||||
|
if (!request.params[1].isNull()) {
|
||||||
|
include_mempool_sequence = request.params[1].get_bool();
|
||||||
|
}
|
||||||
|
|
||||||
|
return MempoolToJSON(EnsureAnyMemPool(request.context), fVerbose, include_mempool_sequence);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
RPCHelpMan getmempoolancestors()
|
||||||
|
{
|
||||||
|
return RPCHelpMan{"getmempoolancestors",
|
||||||
|
"\nIf txid is in the mempool, returns all in-mempool ancestors.\n",
|
||||||
|
{
|
||||||
|
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id (must be in mempool)"},
|
||||||
|
{"verbose", RPCArg::Type::BOOL, RPCArg::Default{false}, "True for a json object, false for array of transaction ids"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
RPCResult{"for verbose = false",
|
||||||
|
RPCResult::Type::ARR, "", "",
|
||||||
|
{{RPCResult::Type::STR_HEX, "", "The transaction id of an in-mempool ancestor transaction"}}},
|
||||||
|
RPCResult{"for verbose = true",
|
||||||
|
RPCResult::Type::OBJ_DYN, "", "",
|
||||||
|
{
|
||||||
|
{RPCResult::Type::OBJ, "transactionid", "", MempoolEntryDescription()},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
RPCExamples{
|
||||||
|
HelpExampleCli("getmempoolancestors", "\"mytxid\"")
|
||||||
|
+ HelpExampleRpc("getmempoolancestors", "\"mytxid\"")
|
||||||
|
},
|
||||||
|
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
||||||
|
{
|
||||||
|
bool fVerbose = false;
|
||||||
|
if (!request.params[1].isNull())
|
||||||
|
fVerbose = request.params[1].get_bool();
|
||||||
|
|
||||||
|
uint256 hash = ParseHashV(request.params[0], "parameter 1");
|
||||||
|
|
||||||
|
const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
|
||||||
|
LOCK(mempool.cs);
|
||||||
|
|
||||||
|
CTxMemPool::txiter it = mempool.mapTx.find(hash);
|
||||||
|
if (it == mempool.mapTx.end()) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool");
|
||||||
|
}
|
||||||
|
|
||||||
|
CTxMemPool::setEntries setAncestors;
|
||||||
|
uint64_t noLimit = std::numeric_limits<uint64_t>::max();
|
||||||
|
std::string dummy;
|
||||||
|
mempool.CalculateMemPoolAncestors(*it, setAncestors, noLimit, noLimit, noLimit, noLimit, dummy, false);
|
||||||
|
|
||||||
|
if (!fVerbose) {
|
||||||
|
UniValue o(UniValue::VARR);
|
||||||
|
for (CTxMemPool::txiter ancestorIt : setAncestors) {
|
||||||
|
o.push_back(ancestorIt->GetTx().GetHash().ToString());
|
||||||
|
}
|
||||||
|
return o;
|
||||||
|
} else {
|
||||||
|
UniValue o(UniValue::VOBJ);
|
||||||
|
for (CTxMemPool::txiter ancestorIt : setAncestors) {
|
||||||
|
const CTxMemPoolEntry &e = *ancestorIt;
|
||||||
|
const uint256& _hash = e.GetTx().GetHash();
|
||||||
|
UniValue info(UniValue::VOBJ);
|
||||||
|
entryToJSON(mempool, info, e);
|
||||||
|
o.pushKV(_hash.ToString(), info);
|
||||||
|
}
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
RPCHelpMan getmempooldescendants()
|
||||||
|
{
|
||||||
|
return RPCHelpMan{"getmempooldescendants",
|
||||||
|
"\nIf txid is in the mempool, returns all in-mempool descendants.\n",
|
||||||
|
{
|
||||||
|
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id (must be in mempool)"},
|
||||||
|
{"verbose", RPCArg::Type::BOOL, RPCArg::Default{false}, "True for a json object, false for array of transaction ids"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
RPCResult{"for verbose = false",
|
||||||
|
RPCResult::Type::ARR, "", "",
|
||||||
|
{{RPCResult::Type::STR_HEX, "", "The transaction id of an in-mempool descendant transaction"}}},
|
||||||
|
RPCResult{"for verbose = true",
|
||||||
|
RPCResult::Type::OBJ_DYN, "", "",
|
||||||
|
{
|
||||||
|
{RPCResult::Type::OBJ, "transactionid", "", MempoolEntryDescription()},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
RPCExamples{
|
||||||
|
HelpExampleCli("getmempooldescendants", "\"mytxid\"")
|
||||||
|
+ HelpExampleRpc("getmempooldescendants", "\"mytxid\"")
|
||||||
|
},
|
||||||
|
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
||||||
|
{
|
||||||
|
bool fVerbose = false;
|
||||||
|
if (!request.params[1].isNull())
|
||||||
|
fVerbose = request.params[1].get_bool();
|
||||||
|
|
||||||
|
uint256 hash = ParseHashV(request.params[0], "parameter 1");
|
||||||
|
|
||||||
|
const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
|
||||||
|
LOCK(mempool.cs);
|
||||||
|
|
||||||
|
CTxMemPool::txiter it = mempool.mapTx.find(hash);
|
||||||
|
if (it == mempool.mapTx.end()) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool");
|
||||||
|
}
|
||||||
|
|
||||||
|
CTxMemPool::setEntries setDescendants;
|
||||||
|
mempool.CalculateDescendants(it, setDescendants);
|
||||||
|
// CTxMemPool::CalculateDescendants will include the given tx
|
||||||
|
setDescendants.erase(it);
|
||||||
|
|
||||||
|
if (!fVerbose) {
|
||||||
|
UniValue o(UniValue::VARR);
|
||||||
|
for (CTxMemPool::txiter descendantIt : setDescendants) {
|
||||||
|
o.push_back(descendantIt->GetTx().GetHash().ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return o;
|
||||||
|
} else {
|
||||||
|
UniValue o(UniValue::VOBJ);
|
||||||
|
for (CTxMemPool::txiter descendantIt : setDescendants) {
|
||||||
|
const CTxMemPoolEntry &e = *descendantIt;
|
||||||
|
const uint256& _hash = e.GetTx().GetHash();
|
||||||
|
UniValue info(UniValue::VOBJ);
|
||||||
|
entryToJSON(mempool, info, e);
|
||||||
|
o.pushKV(_hash.ToString(), info);
|
||||||
|
}
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
RPCHelpMan getmempoolentry()
|
||||||
|
{
|
||||||
|
return RPCHelpMan{"getmempoolentry",
|
||||||
|
"\nReturns mempool data for given transaction\n",
|
||||||
|
{
|
||||||
|
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id (must be in mempool)"},
|
||||||
|
},
|
||||||
|
RPCResult{
|
||||||
|
RPCResult::Type::OBJ, "", "", MempoolEntryDescription()},
|
||||||
|
RPCExamples{
|
||||||
|
HelpExampleCli("getmempoolentry", "\"mytxid\"")
|
||||||
|
+ HelpExampleRpc("getmempoolentry", "\"mytxid\"")
|
||||||
|
},
|
||||||
|
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
||||||
|
{
|
||||||
|
uint256 hash = ParseHashV(request.params[0], "parameter 1");
|
||||||
|
|
||||||
|
const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
|
||||||
|
LOCK(mempool.cs);
|
||||||
|
|
||||||
|
CTxMemPool::txiter it = mempool.mapTx.find(hash);
|
||||||
|
if (it == mempool.mapTx.end()) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool");
|
||||||
|
}
|
||||||
|
|
||||||
|
const CTxMemPoolEntry &e = *it;
|
||||||
|
UniValue info(UniValue::VOBJ);
|
||||||
|
entryToJSON(mempool, info, e);
|
||||||
|
return info;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
UniValue MempoolInfoToJSON(const CTxMemPool& pool)
|
||||||
|
{
|
||||||
|
// Make sure this call is atomic in the pool.
|
||||||
|
LOCK(pool.cs);
|
||||||
|
UniValue ret(UniValue::VOBJ);
|
||||||
|
ret.pushKV("loaded", pool.IsLoaded());
|
||||||
|
ret.pushKV("size", (int64_t)pool.size());
|
||||||
|
ret.pushKV("bytes", (int64_t)pool.GetTotalTxSize());
|
||||||
|
ret.pushKV("usage", (int64_t)pool.DynamicMemoryUsage());
|
||||||
|
ret.pushKV("total_fee", ValueFromAmount(pool.GetTotalFee()));
|
||||||
|
size_t maxmempool = gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
|
||||||
|
ret.pushKV("maxmempool", (int64_t) maxmempool);
|
||||||
|
ret.pushKV("mempoolminfee", ValueFromAmount(std::max(pool.GetMinFee(maxmempool), ::minRelayTxFee).GetFeePerK()));
|
||||||
|
ret.pushKV("minrelaytxfee", ValueFromAmount(::minRelayTxFee.GetFeePerK()));
|
||||||
|
ret.pushKV("unbroadcastcount", uint64_t{pool.GetUnbroadcastTxs().size()});
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
RPCHelpMan getmempoolinfo()
|
||||||
|
{
|
||||||
|
return RPCHelpMan{"getmempoolinfo",
|
||||||
|
"\nReturns details on the active state of the TX memory pool.\n",
|
||||||
|
{},
|
||||||
|
RPCResult{
|
||||||
|
RPCResult::Type::OBJ, "", "",
|
||||||
|
{
|
||||||
|
{RPCResult::Type::BOOL, "loaded", "True if the mempool is fully loaded"},
|
||||||
|
{RPCResult::Type::NUM, "size", "Current tx count"},
|
||||||
|
{RPCResult::Type::NUM, "bytes", "Sum of all virtual transaction sizes as defined in BIP 141. Differs from actual serialized size because witness data is discounted"},
|
||||||
|
{RPCResult::Type::NUM, "usage", "Total memory usage for the mempool"},
|
||||||
|
{RPCResult::Type::STR_AMOUNT, "total_fee", "Total fees for the mempool in " + CURRENCY_UNIT + ", ignoring modified fees through prioritisetransaction"},
|
||||||
|
{RPCResult::Type::NUM, "maxmempool", "Maximum memory usage for the mempool"},
|
||||||
|
{RPCResult::Type::STR_AMOUNT, "mempoolminfee", "Minimum fee rate in " + CURRENCY_UNIT + "/kvB for tx to be accepted. Is the maximum of minrelaytxfee and minimum mempool fee"},
|
||||||
|
{RPCResult::Type::STR_AMOUNT, "minrelaytxfee", "Current minimum relay fee for transactions"},
|
||||||
|
{RPCResult::Type::NUM, "unbroadcastcount", "Current number of transactions that haven't passed initial broadcast yet"}
|
||||||
|
}},
|
||||||
|
RPCExamples{
|
||||||
|
HelpExampleCli("getmempoolinfo", "")
|
||||||
|
+ HelpExampleRpc("getmempoolinfo", "")
|
||||||
|
},
|
||||||
|
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
||||||
|
{
|
||||||
|
return MempoolInfoToJSON(EnsureAnyMemPool(request.context));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
RPCHelpMan savemempool()
|
||||||
|
{
|
||||||
|
return RPCHelpMan{"savemempool",
|
||||||
|
"\nDumps the mempool to disk. It will fail until the previous dump is fully loaded.\n",
|
||||||
|
{},
|
||||||
|
RPCResult{
|
||||||
|
RPCResult::Type::OBJ, "", "",
|
||||||
|
{
|
||||||
|
{RPCResult::Type::STR, "filename", "the directory and file where the mempool was saved"},
|
||||||
|
}},
|
||||||
|
RPCExamples{
|
||||||
|
HelpExampleCli("savemempool", "")
|
||||||
|
+ HelpExampleRpc("savemempool", "")
|
||||||
|
},
|
||||||
|
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
||||||
|
{
|
||||||
|
const ArgsManager& args{EnsureAnyArgsman(request.context)};
|
||||||
|
const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
|
||||||
|
|
||||||
|
if (!mempool.IsLoaded()) {
|
||||||
|
throw JSONRPCError(RPC_MISC_ERROR, "The mempool was not loaded yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!DumpMempool(mempool)) {
|
||||||
|
throw JSONRPCError(RPC_MISC_ERROR, "Unable to dump mempool to disk");
|
||||||
|
}
|
||||||
|
|
||||||
|
UniValue ret(UniValue::VOBJ);
|
||||||
|
ret.pushKV("filename", fs::path((args.GetDataDirNet() / "mempool.dat")).u8string());
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
17
src/rpc/mempool.h
Normal file
17
src/rpc/mempool.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// Copyright (c) 2017-2022 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_RPC_MEMPOOL_H
|
||||||
|
#define BITCOIN_RPC_MEMPOOL_H
|
||||||
|
|
||||||
|
class CTxMemPool;
|
||||||
|
class UniValue;
|
||||||
|
|
||||||
|
/** Mempool information to JSON */
|
||||||
|
UniValue MempoolInfoToJSON(const CTxMemPool& pool);
|
||||||
|
|
||||||
|
/** Mempool to JSON */
|
||||||
|
UniValue MempoolToJSON(const CTxMemPool& pool, bool verbose = false, bool include_mempool_sequence = false);
|
||||||
|
|
||||||
|
#endif // BITCOIN_RPC_MEMPOOL_H
|
Loading…
Reference in New Issue
Block a user