Changeset View
Changeset View
Standalone View
Standalone View
src/rpc/blockchain.cpp
Show All 13 Lines | |||||
#include <coins.h> | #include <coins.h> | ||||
#include <config.h> | #include <config.h> | ||||
#include <consensus/validation.h> | #include <consensus/validation.h> | ||||
#include <core_io.h> | #include <core_io.h> | ||||
#include <hash.h> | #include <hash.h> | ||||
#include <index/blockfilterindex.h> | #include <index/blockfilterindex.h> | ||||
#include <node/coinstats.h> | #include <node/coinstats.h> | ||||
#include <node/context.h> | #include <node/context.h> | ||||
#include <node/utxo_snapshot.h> | |||||
#include <policy/policy.h> | #include <policy/policy.h> | ||||
#include <primitives/transaction.h> | #include <primitives/transaction.h> | ||||
#include <rpc/server.h> | #include <rpc/server.h> | ||||
#include <rpc/util.h> | #include <rpc/util.h> | ||||
#include <script/descriptor.h> | #include <script/descriptor.h> | ||||
#include <streams.h> | #include <streams.h> | ||||
#include <txdb.h> | #include <txdb.h> | ||||
#include <txmempool.h> | #include <txmempool.h> | ||||
▲ Show 20 Lines • Show All 2,628 Lines • ▼ Show 20 Lines | static UniValue getblockfilter(const Config &config, | ||||
} | } | ||||
UniValue ret(UniValue::VOBJ); | UniValue ret(UniValue::VOBJ); | ||||
ret.pushKV("filter", HexStr(filter.GetEncodedFilter())); | ret.pushKV("filter", HexStr(filter.GetEncodedFilter())); | ||||
ret.pushKV("header", filter_header.GetHex()); | ret.pushKV("header", filter_header.GetHex()); | ||||
return ret; | return ret; | ||||
} | } | ||||
/** | |||||
* Serialize the UTXO set to a file for loading elsewhere. | |||||
* | |||||
* @see SnapshotMetadata | |||||
*/ | |||||
static UniValue dumptxoutset(const Config &config, | |||||
const JSONRPCRequest &request) { | |||||
RPCHelpMan{"dumptxoutset", | |||||
"\nWrite the serialized UTXO set to disk.\n" | |||||
"Incidentally flushes the latest coinsdb (leveldb) to disk.\n", | |||||
{ | |||||
{"path", RPCArg::Type::STR, RPCArg::Optional::NO, | |||||
/* default_val */ "", | |||||
"path to the output file. If relative, will be prefixed by " | |||||
"datadir."}, | |||||
}, | |||||
RPCResult{"{\n" | |||||
" \"coins_written\": n, (numeric) the number of " | |||||
"coins written in the snapshot\n" | |||||
" \"base_hash\": \"...\", (string) the hash of the " | |||||
"base of the snapshot\n" | |||||
" \"base_height\": n, (string) the height of the " | |||||
"base of the snapshot\n" | |||||
" \"path\": \"...\" (string) the absolute " | |||||
"path that the snapshot was written to\n" | |||||
"]\n"}, | |||||
RPCExamples{HelpExampleCli("dumptxoutset", "utxo.dat")}} | |||||
.Check(request); | |||||
fs::path path = fs::absolute(request.params[0].get_str(), GetDataDir()); | |||||
// Write to a temporary path and then move into `path` on completion | |||||
// to avoid confusion due to an interruption. | |||||
fs::path temppath = | |||||
fs::absolute(request.params[0].get_str() + ".incomplete", GetDataDir()); | |||||
if (fs::exists(path)) { | |||||
throw JSONRPCError( | |||||
RPC_INVALID_PARAMETER, | |||||
path.string() + | |||||
" already exists. If you are sure this is what you want, " | |||||
"move it out of the way first"); | |||||
} | |||||
FILE *file{fsbridge::fopen(temppath, "wb")}; | |||||
CAutoFile afile{file, SER_DISK, CLIENT_VERSION}; | |||||
std::unique_ptr<CCoinsViewCursor> pcursor; | |||||
CCoinsStats stats; | |||||
CBlockIndex *tip; | |||||
{ | |||||
// We need to lock cs_main to ensure that the coinsdb isn't written to | |||||
// between (i) flushing coins cache to disk (coinsdb), (ii) getting | |||||
// stats based upon the coinsdb, and (iii) constructing a cursor to the | |||||
// coinsdb for use below this block. | |||||
// | |||||
// Cursors returned by leveldb iterate over snapshots, so the contents | |||||
// of the pcursor will not be affected by simultaneous writes during | |||||
// use below this block. | |||||
// | |||||
// See discussion here: | |||||
// https://github.com/bitcoin/bitcoin/pull/15606#discussion_r274479369 | |||||
// | |||||
LOCK(::cs_main); | |||||
::ChainstateActive().ForceFlushStateToDisk(); | |||||
if (!GetUTXOStats(&::ChainstateActive().CoinsDB(), stats)) { | |||||
throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set"); | |||||
} | |||||
pcursor = std::unique_ptr<CCoinsViewCursor>( | |||||
::ChainstateActive().CoinsDB().Cursor()); | |||||
tip = LookupBlockIndex(stats.hashBlock); | |||||
CHECK_NONFATAL(tip); | |||||
} | |||||
SnapshotMetadata metadata{tip->GetBlockHash(), stats.coins_count, | |||||
uint64_t(tip->GetChainTxCount())}; | |||||
afile << metadata; | |||||
COutPoint key; | |||||
Coin coin; | |||||
unsigned int iter{0}; | |||||
while (pcursor->Valid()) { | |||||
if (iter % 5000 == 0 && !IsRPCRunning()) { | |||||
throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Shutting down"); | |||||
} | |||||
++iter; | |||||
if (pcursor->GetKey(key) && pcursor->GetValue(coin)) { | |||||
afile << key; | |||||
afile << coin; | |||||
} | |||||
pcursor->Next(); | |||||
} | |||||
afile.fclose(); | |||||
fs::rename(temppath, path); | |||||
UniValue result(UniValue::VOBJ); | |||||
result.pushKV("coins_written", stats.coins_count); | |||||
result.pushKV("base_hash", tip->GetBlockHash().ToString()); | |||||
result.pushKV("base_height", tip->nHeight); | |||||
result.pushKV("path", path.string()); | |||||
return result; | |||||
} | |||||
// clang-format off | // clang-format off | ||||
static const CRPCCommand commands[] = { | static const CRPCCommand commands[] = { | ||||
// category name actor (function) argNames | // category name actor (function) argNames | ||||
// ------------------- ------------------------ ---------------------- ---------- | // ------------------- ------------------------ ---------------------- ---------- | ||||
{ "blockchain", "getbestblockhash", getbestblockhash, {} }, | { "blockchain", "getbestblockhash", getbestblockhash, {} }, | ||||
{ "blockchain", "getblock", getblock, {"blockhash","verbosity|verbose"} }, | { "blockchain", "getblock", getblock, {"blockhash","verbosity|verbose"} }, | ||||
{ "blockchain", "getblockchaininfo", getblockchaininfo, {} }, | { "blockchain", "getblockchaininfo", getblockchaininfo, {} }, | ||||
{ "blockchain", "getblockcount", getblockcount, {} }, | { "blockchain", "getblockcount", getblockcount, {} }, | ||||
Show All 19 Lines | static const CRPCCommand commands[] = { | ||||
/* Not shown in help */ | /* Not shown in help */ | ||||
{ "hidden", "getfinalizedblockhash", getfinalizedblockhash, {} }, | { "hidden", "getfinalizedblockhash", getfinalizedblockhash, {} }, | ||||
{ "hidden", "finalizeblock", finalizeblock, {"blockhash"} }, | { "hidden", "finalizeblock", finalizeblock, {"blockhash"} }, | ||||
{ "hidden", "invalidateblock", invalidateblock, {"blockhash"} }, | { "hidden", "invalidateblock", invalidateblock, {"blockhash"} }, | ||||
{ "hidden", "parkblock", parkblock, {"blockhash"} }, | { "hidden", "parkblock", parkblock, {"blockhash"} }, | ||||
{ "hidden", "reconsiderblock", reconsiderblock, {"blockhash"} }, | { "hidden", "reconsiderblock", reconsiderblock, {"blockhash"} }, | ||||
{ "hidden", "syncwithvalidationinterfacequeue", syncwithvalidationinterfacequeue, {} }, | { "hidden", "syncwithvalidationinterfacequeue", syncwithvalidationinterfacequeue, {} }, | ||||
{ "hidden", "dumptxoutset", dumptxoutset, {"path"} }, | |||||
{ "hidden", "unparkblock", unparkblock, {"blockhash"} }, | { "hidden", "unparkblock", unparkblock, {"blockhash"} }, | ||||
{ "hidden", "waitfornewblock", waitfornewblock, {"timeout"} }, | { "hidden", "waitfornewblock", waitfornewblock, {"timeout"} }, | ||||
{ "hidden", "waitforblock", waitforblock, {"blockhash","timeout"} }, | { "hidden", "waitforblock", waitforblock, {"blockhash","timeout"} }, | ||||
{ "hidden", "waitforblockheight", waitforblockheight, {"height","timeout"} }, | { "hidden", "waitforblockheight", waitforblockheight, {"height","timeout"} }, | ||||
}; | }; | ||||
// clang-format on | // clang-format on | ||||
void RegisterBlockchainRPCCommands(CRPCTable &t) { | void RegisterBlockchainRPCCommands(CRPCTable &t) { | ||||
for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) { | for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) { | ||||
t.appendCommand(commands[vcidx].name, &commands[vcidx]); | t.appendCommand(commands[vcidx].name, &commands[vcidx]); | ||||
} | } | ||||
} | } | ||||
NodeContext *g_rpc_node = nullptr; | NodeContext *g_rpc_node = nullptr; |