diff --git a/src/rest.cpp b/src/rest.cpp --- a/src/rest.cpp +++ b/src/rest.cpp @@ -346,7 +346,8 @@ case RetFormat::JSON: { JSONRPCRequest jsonRequest(context); jsonRequest.params = UniValue(UniValue::VARR); - UniValue chainInfoObject = getblockchaininfo(config, jsonRequest); + UniValue chainInfoObject = + getblockchaininfo().HandleRequest(config, jsonRequest); std::string strJSON = chainInfoObject.write() + "\n"; req->WriteHeader("Content-Type", "application/json"); req->WriteReply(HTTP_OK, strJSON); diff --git a/src/rpc/blockchain.h b/src/rpc/blockchain.h --- a/src/rpc/blockchain.h +++ b/src/rpc/blockchain.h @@ -12,9 +12,8 @@ class CBlock; class CBlockIndex; class ChainstateManager; -class Config; class CTxMemPool; -class JSONRPCRequest; +class RPCHelpMan; struct NodeContext; namespace util { class Ref; @@ -22,7 +21,7 @@ extern RecursiveMutex cs_main; -UniValue getblockchaininfo(const Config &config, const JSONRPCRequest &request); +RPCHelpMan getblockchaininfo(); /** * Get the required difficulty of the next block w/r/t the given block index. diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -189,9 +189,8 @@ return result; } -static UniValue getblockcount(const Config &config, - const JSONRPCRequest &request) { - RPCHelpMan{ +static RPCHelpMan getblockcount() { + return RPCHelpMan{ "getblockcount", "Returns the height of the most-work fully-validated chain.\n" "The genesis block has height 0.\n", @@ -199,16 +198,16 @@ RPCResult{RPCResult::Type::NUM, "", "The current block count"}, RPCExamples{HelpExampleCli("getblockcount", "") + HelpExampleRpc("getblockcount", "")}, - } - .Check(request); - - LOCK(cs_main); - return ::ChainActive().Height(); + [&](const RPCHelpMan &self, const Config &config, + const JSONRPCRequest &request) -> UniValue { + LOCK(cs_main); + return ::ChainActive().Height(); + }, + }; } -static UniValue getbestblockhash(const Config &config, - const JSONRPCRequest &request) { - RPCHelpMan{ +static RPCHelpMan getbestblockhash() { + return RPCHelpMan{ "getbestblockhash", "Returns the hash of the best (tip) block in the " "most-work fully-validated chain.\n", @@ -216,32 +215,33 @@ RPCResult{RPCResult::Type::STR_HEX, "", "the block hash, hex-encoded"}, RPCExamples{HelpExampleCli("getbestblockhash", "") + HelpExampleRpc("getbestblockhash", "")}, - } - .Check(request); - - LOCK(cs_main); - return ::ChainActive().Tip()->GetBlockHash().GetHex(); + [&](const RPCHelpMan &self, const Config &config, + const JSONRPCRequest &request) -> UniValue { + LOCK(cs_main); + return ::ChainActive().Tip()->GetBlockHash().GetHex(); + }, + }; } -UniValue getfinalizedblockhash(const Config &config, - const JSONRPCRequest &request) { - RPCHelpMan{ +RPCHelpMan getfinalizedblockhash() { + return RPCHelpMan{ "getfinalizedblockhash", "Returns the hash of the currently finalized block\n", {}, RPCResult{RPCResult::Type::STR_HEX, "", "the block hash, hex-encoded"}, RPCExamples{HelpExampleCli("getfinalizedblockhash", "") + HelpExampleRpc("getfinalizedblockhash", "")}, - } - .Check(request); - - LOCK(cs_main); - const CBlockIndex *blockIndexFinalized = - ::ChainstateActive().GetFinalizedBlock(); - if (blockIndexFinalized) { - return blockIndexFinalized->GetBlockHash().GetHex(); - } - return UniValue(UniValue::VSTR); + [&](const RPCHelpMan &self, const Config &config, + const JSONRPCRequest &request) -> UniValue { + LOCK(cs_main); + const CBlockIndex *blockIndexFinalized = + ::ChainstateActive().GetFinalizedBlock(); + if (blockIndexFinalized) { + return blockIndexFinalized->GetBlockHash().GetHex(); + } + return UniValue(UniValue::VSTR); + }, + }; } void RPCNotifyBlockChange(const CBlockIndex *pindex) { @@ -253,9 +253,8 @@ cond_blockchange.notify_all(); } -static UniValue waitfornewblock(const Config &config, - const JSONRPCRequest &request) { - RPCHelpMan{ +static RPCHelpMan waitfornewblock() { + return RPCHelpMan{ "waitfornewblock", "Waits for a specific new block and returns useful info about it.\n" "\nReturns the current block on timeout or exit.\n", @@ -273,43 +272,46 @@ }}, RPCExamples{HelpExampleCli("waitfornewblock", "1000") + HelpExampleRpc("waitfornewblock", "1000")}, - } - .Check(request); - - int timeout = 0; - if (!request.params[0].isNull()) { - timeout = request.params[0].get_int(); - } + [&](const RPCHelpMan &self, const Config &config, + const JSONRPCRequest &request) -> UniValue { + int timeout = 0; + if (!request.params[0].isNull()) { + timeout = request.params[0].get_int(); + } - CUpdatedBlock block; - { - WAIT_LOCK(cs_blockchange, lock); - block = latestblock; - if (timeout) { - cond_blockchange.wait_for( - lock, std::chrono::milliseconds(timeout), - [&block]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) { - return latestblock.height != block.height || - latestblock.hash != block.hash || !IsRPCRunning(); - }); - } else { - cond_blockchange.wait( - lock, [&block]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) { - return latestblock.height != block.height || - latestblock.hash != block.hash || !IsRPCRunning(); - }); - } - block = latestblock; - } - UniValue ret(UniValue::VOBJ); - ret.pushKV("hash", block.hash.GetHex()); - ret.pushKV("height", block.height); - return ret; + CUpdatedBlock block; + { + WAIT_LOCK(cs_blockchange, lock); + block = latestblock; + if (timeout) { + cond_blockchange.wait_for( + lock, std::chrono::milliseconds(timeout), + [&block]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) { + return latestblock.height != block.height || + latestblock.hash != block.hash || + !IsRPCRunning(); + }); + } else { + cond_blockchange.wait( + lock, + [&block]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) { + return latestblock.height != block.height || + latestblock.hash != block.hash || + !IsRPCRunning(); + }); + } + block = latestblock; + } + UniValue ret(UniValue::VOBJ); + ret.pushKV("hash", block.hash.GetHex()); + ret.pushKV("height", block.height); + return ret; + }, + }; } -static UniValue waitforblock(const Config &config, - const JSONRPCRequest &request) { - RPCHelpMan{ +static RPCHelpMan waitforblock() { + return RPCHelpMan{ "waitforblock", "Waits for a specific new block and returns useful info about it.\n" "\nReturns the current block on timeout or exit.\n", @@ -333,44 +335,45 @@ HelpExampleRpc("waitforblock", "\"0000000000079f8ef3d2c688c244eb7a4570b24c9" "ed7b4a8c619eb02596f8862\", 1000")}, - } - .Check(request); + [&](const RPCHelpMan &self, const Config &config, + const JSONRPCRequest &request) -> UniValue { + int timeout = 0; - int timeout = 0; + BlockHash hash(ParseHashV(request.params[0], "blockhash")); - BlockHash hash(ParseHashV(request.params[0], "blockhash")); - - if (!request.params[1].isNull()) { - timeout = request.params[1].get_int(); - } + if (!request.params[1].isNull()) { + timeout = request.params[1].get_int(); + } - CUpdatedBlock block; - { - WAIT_LOCK(cs_blockchange, lock); - if (timeout) { - cond_blockchange.wait_for( - lock, std::chrono::milliseconds(timeout), - [&hash]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) { - return latestblock.hash == hash || !IsRPCRunning(); - }); - } else { - cond_blockchange.wait( - lock, [&hash]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) { - return latestblock.hash == hash || !IsRPCRunning(); - }); - } - block = latestblock; - } + CUpdatedBlock block; + { + WAIT_LOCK(cs_blockchange, lock); + if (timeout) { + cond_blockchange.wait_for( + lock, std::chrono::milliseconds(timeout), + [&hash]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) { + return latestblock.hash == hash || !IsRPCRunning(); + }); + } else { + cond_blockchange.wait( + lock, + [&hash]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) { + return latestblock.hash == hash || !IsRPCRunning(); + }); + } + block = latestblock; + } - UniValue ret(UniValue::VOBJ); - ret.pushKV("hash", block.hash.GetHex()); - ret.pushKV("height", block.height); - return ret; + UniValue ret(UniValue::VOBJ); + ret.pushKV("hash", block.hash.GetHex()); + ret.pushKV("height", block.height); + return ret; + }, + }; } -static UniValue waitforblockheight(const Config &config, - const JSONRPCRequest &request) { - RPCHelpMan{ +static RPCHelpMan waitforblockheight() { + return RPCHelpMan{ "waitforblockheight", "Waits for (at least) block height and returns the height and " "hash\nof the current tip.\n" @@ -391,44 +394,46 @@ }}, RPCExamples{HelpExampleCli("waitforblockheight", "100 1000") + HelpExampleRpc("waitforblockheight", "100, 1000")}, - } - .Check(request); - - int timeout = 0; + [&](const RPCHelpMan &self, const Config &config, + const JSONRPCRequest &request) -> UniValue { + int timeout = 0; - int height = request.params[0].get_int(); + int height = request.params[0].get_int(); - if (!request.params[1].isNull()) { - timeout = request.params[1].get_int(); - } + if (!request.params[1].isNull()) { + timeout = request.params[1].get_int(); + } - CUpdatedBlock block; - { - WAIT_LOCK(cs_blockchange, lock); - if (timeout) { - cond_blockchange.wait_for( - lock, std::chrono::milliseconds(timeout), - [&height]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) { - return latestblock.height >= height || !IsRPCRunning(); - }); - } else { - cond_blockchange.wait( - lock, [&height]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) { - return latestblock.height >= height || !IsRPCRunning(); - }); - } - block = latestblock; - } - UniValue ret(UniValue::VOBJ); - ret.pushKV("hash", block.hash.GetHex()); - ret.pushKV("height", block.height); - return ret; + CUpdatedBlock block; + { + WAIT_LOCK(cs_blockchange, lock); + if (timeout) { + cond_blockchange.wait_for( + lock, std::chrono::milliseconds(timeout), + [&height]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) { + return latestblock.height >= height || + !IsRPCRunning(); + }); + } else { + cond_blockchange.wait( + lock, + [&height]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) { + return latestblock.height >= height || + !IsRPCRunning(); + }); + } + block = latestblock; + } + UniValue ret(UniValue::VOBJ); + ret.pushKV("hash", block.hash.GetHex()); + ret.pushKV("height", block.height); + return ret; + }, + }; } -static UniValue -syncwithvalidationinterfacequeue(const Config &config, - const JSONRPCRequest &request) { - RPCHelpMan{ +static RPCHelpMan syncwithvalidationinterfacequeue() { + return RPCHelpMan{ "syncwithvalidationinterfacequeue", "Waits for the validation interface queue to catch up on everything " "that was there when we entered this function.\n", @@ -436,16 +441,16 @@ RPCResult{RPCResult::Type::NONE, "", ""}, RPCExamples{HelpExampleCli("syncwithvalidationinterfacequeue", "") + HelpExampleRpc("syncwithvalidationinterfacequeue", "")}, - } - .Check(request); - - SyncWithValidationInterfaceQueue(); - return NullUniValue; + [&](const RPCHelpMan &self, const Config &config, + const JSONRPCRequest &request) -> UniValue { + SyncWithValidationInterfaceQueue(); + return NullUniValue; + }, + }; } -static UniValue getdifficulty(const Config &config, - const JSONRPCRequest &request) { - RPCHelpMan{ +static RPCHelpMan getdifficulty() { + return RPCHelpMan{ "getdifficulty", "Returns the proof-of-work difficulty as a multiple of the minimum " "difficulty.\n", @@ -455,11 +460,12 @@ "difficulty."}, RPCExamples{HelpExampleCli("getdifficulty", "") + HelpExampleRpc("getdifficulty", "")}, - } - .Check(request); - - LOCK(cs_main); - return GetDifficulty(::ChainActive().Tip()); + [&](const RPCHelpMan &self, const Config &config, + const JSONRPCRequest &request) -> UniValue { + LOCK(cs_main); + return GetDifficulty(::ChainActive().Tip()); + }, + }; } static std::vector MempoolEntryDescription() { @@ -607,9 +613,8 @@ } } -static UniValue getrawmempool(const Config &config, - const JSONRPCRequest &request) { - RPCHelpMan{ +static RPCHelpMan getrawmempool() { + return RPCHelpMan{ "getrawmempool", "Returns all transaction ids in memory pool as a json array of " "string transaction ids.\n" @@ -638,20 +643,20 @@ }, RPCExamples{HelpExampleCli("getrawmempool", "true") + HelpExampleRpc("getrawmempool", "true")}, - } - .Check(request); - - bool fVerbose = false; - if (!request.params[0].isNull()) { - fVerbose = request.params[0].get_bool(); - } + [&](const RPCHelpMan &self, const Config &config, + const JSONRPCRequest &request) -> UniValue { + bool fVerbose = false; + if (!request.params[0].isNull()) { + fVerbose = request.params[0].get_bool(); + } - return MempoolToJSON(EnsureMemPool(request.context), fVerbose); + return MempoolToJSON(EnsureMemPool(request.context), fVerbose); + }, + }; } -static UniValue getmempoolancestors(const Config &config, - const JSONRPCRequest &request) { - RPCHelpMan{ +static RPCHelpMan getmempoolancestors() { + return RPCHelpMan{ "getmempoolancestors", "If txid is in the mempool, returns all in-mempool ancestors.\n", { @@ -679,53 +684,54 @@ }, RPCExamples{HelpExampleCli("getmempoolancestors", "\"mytxid\"") + HelpExampleRpc("getmempoolancestors", "\"mytxid\"")}, - } - .Check(request); - - bool fVerbose = false; - if (!request.params[1].isNull()) { - fVerbose = request.params[1].get_bool(); - } - - TxId txid(ParseHashV(request.params[0], "parameter 1")); + [&](const RPCHelpMan &self, const Config &config, + const JSONRPCRequest &request) -> UniValue { + bool fVerbose = false; + if (!request.params[1].isNull()) { + fVerbose = request.params[1].get_bool(); + } - const CTxMemPool &mempool = EnsureMemPool(request.context); - LOCK(mempool.cs); + TxId txid(ParseHashV(request.params[0], "parameter 1")); - CTxMemPool::txiter it = mempool.mapTx.find(txid); - if (it == mempool.mapTx.end()) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, - "Transaction not in mempool"); - } + const CTxMemPool &mempool = EnsureMemPool(request.context); + LOCK(mempool.cs); - CTxMemPool::setEntries setAncestors; - uint64_t noLimit = std::numeric_limits::max(); - std::string dummy; - mempool.CalculateMemPoolAncestors(*it, setAncestors, noLimit, noLimit, - noLimit, noLimit, dummy, false); + CTxMemPool::txiter it = mempool.mapTx.find(txid); + if (it == mempool.mapTx.end()) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, + "Transaction not in mempool"); + } - if (!fVerbose) { - UniValue o(UniValue::VARR); - for (CTxMemPool::txiter ancestorIt : setAncestors) { - o.push_back(ancestorIt->GetTx().GetId().ToString()); - } - return o; - } else { - UniValue o(UniValue::VOBJ); - for (CTxMemPool::txiter ancestorIt : setAncestors) { - const CTxMemPoolEntry &e = *ancestorIt; - const TxId &_txid = e.GetTx().GetId(); - UniValue info(UniValue::VOBJ); - entryToJSON(mempool, info, e); - o.pushKV(_txid.ToString(), info); - } - return o; - } + CTxMemPool::setEntries setAncestors; + uint64_t noLimit = std::numeric_limits::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().GetId().ToString()); + } + return o; + } else { + UniValue o(UniValue::VOBJ); + for (CTxMemPool::txiter ancestorIt : setAncestors) { + const CTxMemPoolEntry &e = *ancestorIt; + const TxId &_txid = e.GetTx().GetId(); + UniValue info(UniValue::VOBJ); + entryToJSON(mempool, info, e); + o.pushKV(_txid.ToString(), info); + } + return o; + } + }, + }; } -static UniValue getmempooldescendants(const Config &config, - const JSONRPCRequest &request) { - RPCHelpMan{ +static RPCHelpMan getmempooldescendants() { + return RPCHelpMan{ "getmempooldescendants", "If txid is in the mempool, returns all in-mempool descendants.\n", { @@ -753,53 +759,53 @@ }, RPCExamples{HelpExampleCli("getmempooldescendants", "\"mytxid\"") + HelpExampleRpc("getmempooldescendants", "\"mytxid\"")}, - } - .Check(request); - - bool fVerbose = false; - if (!request.params[1].isNull()) { - fVerbose = request.params[1].get_bool(); - } - - TxId txid(ParseHashV(request.params[0], "parameter 1")); - - const CTxMemPool &mempool = EnsureMemPool(request.context); - LOCK(mempool.cs); + [&](const RPCHelpMan &self, const Config &config, + const JSONRPCRequest &request) -> UniValue { + bool fVerbose = false; + if (!request.params[1].isNull()) { + fVerbose = request.params[1].get_bool(); + } - CTxMemPool::txiter it = mempool.mapTx.find(txid); - if (it == mempool.mapTx.end()) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, - "Transaction not in mempool"); - } + TxId txid(ParseHashV(request.params[0], "parameter 1")); - CTxMemPool::setEntries setDescendants; - mempool.CalculateDescendants(it, setDescendants); - // CTxMemPool::CalculateDescendants will include the given tx - setDescendants.erase(it); + const CTxMemPool &mempool = EnsureMemPool(request.context); + LOCK(mempool.cs); - if (!fVerbose) { - UniValue o(UniValue::VARR); - for (CTxMemPool::txiter descendantIt : setDescendants) { - o.push_back(descendantIt->GetTx().GetId().ToString()); - } + CTxMemPool::txiter it = mempool.mapTx.find(txid); + if (it == mempool.mapTx.end()) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, + "Transaction not in mempool"); + } - return o; - } else { - UniValue o(UniValue::VOBJ); - for (CTxMemPool::txiter descendantIt : setDescendants) { - const CTxMemPoolEntry &e = *descendantIt; - const TxId &_txid = e.GetTx().GetId(); - UniValue info(UniValue::VOBJ); - entryToJSON(mempool, info, e); - o.pushKV(_txid.ToString(), info); - } - return o; - } + 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().GetId().ToString()); + } + + return o; + } else { + UniValue o(UniValue::VOBJ); + for (CTxMemPool::txiter descendantIt : setDescendants) { + const CTxMemPoolEntry &e = *descendantIt; + const TxId &_txid = e.GetTx().GetId(); + UniValue info(UniValue::VOBJ); + entryToJSON(mempool, info, e); + o.pushKV(_txid.ToString(), info); + } + return o; + } + }, + }; } -static UniValue getmempoolentry(const Config &config, - const JSONRPCRequest &request) { - RPCHelpMan{ +static RPCHelpMan getmempoolentry() { + return RPCHelpMan{ "getmempoolentry", "Returns mempool data for given transaction\n", { @@ -809,29 +815,29 @@ RPCResult{RPCResult::Type::OBJ, "", "", MempoolEntryDescription()}, RPCExamples{HelpExampleCli("getmempoolentry", "\"mytxid\"") + HelpExampleRpc("getmempoolentry", "\"mytxid\"")}, - } - .Check(request); - - TxId txid(ParseHashV(request.params[0], "parameter 1")); + [&](const RPCHelpMan &self, const Config &config, + const JSONRPCRequest &request) -> UniValue { + TxId txid(ParseHashV(request.params[0], "parameter 1")); - const CTxMemPool &mempool = EnsureMemPool(request.context); - LOCK(mempool.cs); + const CTxMemPool &mempool = EnsureMemPool(request.context); + LOCK(mempool.cs); - CTxMemPool::txiter it = mempool.mapTx.find(txid); - if (it == mempool.mapTx.end()) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, - "Transaction not in mempool"); - } + CTxMemPool::txiter it = mempool.mapTx.find(txid); + 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; + const CTxMemPoolEntry &e = *it; + UniValue info(UniValue::VOBJ); + entryToJSON(mempool, info, e); + return info; + }, + }; } -static UniValue getblockhash(const Config &config, - const JSONRPCRequest &request) { - RPCHelpMan{ +static RPCHelpMan getblockhash() { + return RPCHelpMan{ "getblockhash", "Returns hash of block in best-block-chain at height provided.\n", { @@ -841,23 +847,24 @@ RPCResult{RPCResult::Type::STR_HEX, "", "The block hash"}, RPCExamples{HelpExampleCli("getblockhash", "1000") + HelpExampleRpc("getblockhash", "1000")}, - } - .Check(request); - - LOCK(cs_main); + [&](const RPCHelpMan &self, const Config &config, + const JSONRPCRequest &request) -> UniValue { + LOCK(cs_main); - int nHeight = request.params[0].get_int(); - if (nHeight < 0 || nHeight > ::ChainActive().Height()) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); - } + int nHeight = request.params[0].get_int(); + if (nHeight < 0 || nHeight > ::ChainActive().Height()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, + "Block height out of range"); + } - CBlockIndex *pblockindex = ::ChainActive()[nHeight]; - return pblockindex->GetBlockHash().GetHex(); + CBlockIndex *pblockindex = ::ChainActive()[nHeight]; + return pblockindex->GetBlockHash().GetHex(); + }, + }; } -static UniValue getblockheader(const Config &config, - const JSONRPCRequest &request) { - RPCHelpMan{ +static RPCHelpMan getblockheader() { + return RPCHelpMan{ "getblockheader", "If verbose is false, returns a string that is serialized, hex-encoded " "data for blockheader 'hash'.\n" @@ -914,36 +921,38 @@ HelpExampleRpc("getblockheader", "\"00000000c937983704a73af28acdec37b049d214a" "dbda81d7e2a3dd146f6ed09\"")}, - } - .Check(request); - - BlockHash hash(ParseHashV(request.params[0], "hash")); + [&](const RPCHelpMan &self, const Config &config, + const JSONRPCRequest &request) -> UniValue { + BlockHash hash(ParseHashV(request.params[0], "hash")); - bool fVerbose = true; - if (!request.params[1].isNull()) { - fVerbose = request.params[1].get_bool(); - } + bool fVerbose = true; + if (!request.params[1].isNull()) { + fVerbose = request.params[1].get_bool(); + } - const CBlockIndex *pblockindex; - const CBlockIndex *tip; - { - LOCK(cs_main); - pblockindex = LookupBlockIndex(hash); - tip = ::ChainActive().Tip(); - } + const CBlockIndex *pblockindex; + const CBlockIndex *tip; + { + LOCK(cs_main); + pblockindex = LookupBlockIndex(hash); + tip = ::ChainActive().Tip(); + } - if (!pblockindex) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); - } + if (!pblockindex) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, + "Block not found"); + } - if (!fVerbose) { - CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION); - ssBlock << pblockindex->GetBlockHeader(); - std::string strHex = HexStr(ssBlock); - return strHex; - } + if (!fVerbose) { + CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION); + ssBlock << pblockindex->GetBlockHeader(); + std::string strHex = HexStr(ssBlock); + return strHex; + } - return blockheaderToJSON(tip, pblockindex); + return blockheaderToJSON(tip, pblockindex); + }, + }; } static CBlock GetBlockChecked(const Config &config, @@ -978,8 +987,8 @@ return blockUndo; } -static UniValue getblock(const Config &config, const JSONRPCRequest &request) { - RPCHelpMan{ +static RPCHelpMan getblock() { + return RPCHelpMan{ "getblock", "If verbosity is 0 or false, returns a string that is serialized, " "hex-encoded data for block 'hash'.\n" @@ -1067,49 +1076,50 @@ "214adbda81d7e2a3dd146f6ed09\"") + HelpExampleRpc("getblock", "\"00000000c937983704a73af28acdec37b049d" "214adbda81d7e2a3dd146f6ed09\"")}, - } - .Check(request); - - BlockHash hash(ParseHashV(request.params[0], "blockhash")); + [&](const RPCHelpMan &self, const Config &config, + const JSONRPCRequest &request) -> UniValue { + BlockHash hash(ParseHashV(request.params[0], "blockhash")); + + int verbosity = 1; + if (!request.params[1].isNull()) { + if (request.params[1].isNum()) { + verbosity = request.params[1].get_int(); + } else { + verbosity = request.params[1].get_bool() ? 1 : 0; + } + } - int verbosity = 1; - if (!request.params[1].isNull()) { - if (request.params[1].isNum()) { - verbosity = request.params[1].get_int(); - } else { - verbosity = request.params[1].get_bool() ? 1 : 0; - } - } + CBlock block; + const CBlockIndex *pblockindex; + const CBlockIndex *tip; + { + LOCK(cs_main); + pblockindex = LookupBlockIndex(hash); + tip = ::ChainActive().Tip(); - CBlock block; - const CBlockIndex *pblockindex; - const CBlockIndex *tip; - { - LOCK(cs_main); - pblockindex = LookupBlockIndex(hash); - tip = ::ChainActive().Tip(); - - if (!pblockindex) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); - } + if (!pblockindex) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, + "Block not found"); + } - block = GetBlockChecked(config, pblockindex); - } + block = GetBlockChecked(config, pblockindex); + } - if (verbosity <= 0) { - CDataStream ssBlock(SER_NETWORK, - PROTOCOL_VERSION | RPCSerializationFlags()); - ssBlock << block; - std::string strHex = HexStr(ssBlock); - return strHex; - } + if (verbosity <= 0) { + CDataStream ssBlock(SER_NETWORK, + PROTOCOL_VERSION | RPCSerializationFlags()); + ssBlock << block; + std::string strHex = HexStr(ssBlock); + return strHex; + } - return blockToJSON(block, tip, pblockindex, verbosity >= 2); + return blockToJSON(block, tip, pblockindex, verbosity >= 2); + }, + }; } -static UniValue pruneblockchain(const Config &config, - const JSONRPCRequest &request) { - RPCHelpMan{ +static RPCHelpMan pruneblockchain() { + return RPCHelpMan{ "pruneblockchain", "", { @@ -1124,64 +1134,67 @@ RPCResult{RPCResult::Type::NUM, "", "Height of the last block pruned"}, RPCExamples{HelpExampleCli("pruneblockchain", "1000") + HelpExampleRpc("pruneblockchain", "1000")}, - } - .Check(request); - - if (!fPruneMode) { - throw JSONRPCError( - RPC_MISC_ERROR, - "Cannot prune blocks because node is not in prune mode."); - } + [&](const RPCHelpMan &self, const Config &config, + const JSONRPCRequest &request) -> UniValue { + if (!fPruneMode) { + throw JSONRPCError( + RPC_MISC_ERROR, + "Cannot prune blocks because node is not in prune mode."); + } - LOCK(cs_main); + LOCK(cs_main); - int heightParam = request.params[0].get_int(); - if (heightParam < 0) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative block height."); - } + int heightParam = request.params[0].get_int(); + if (heightParam < 0) { + throw JSONRPCError(RPC_INVALID_PARAMETER, + "Negative block height."); + } - // Height value more than a billion is too high to be a block height, and - // too low to be a block time (corresponds to timestamp from Sep 2001). - if (heightParam > 1000000000) { - // Add a 2 hour buffer to include blocks which might have had old - // timestamps - CBlockIndex *pindex = ::ChainActive().FindEarliestAtLeast( - heightParam - TIMESTAMP_WINDOW, 0); - if (!pindex) { - throw JSONRPCError( - RPC_INVALID_PARAMETER, - "Could not find block with at least the specified timestamp."); - } - heightParam = pindex->nHeight; - } + // Height value more than a billion is too high to be a block + // height, and too low to be a block time (corresponds to timestamp + // from Sep 2001). + if (heightParam > 1000000000) { + // Add a 2 hour buffer to include blocks which might have had + // old timestamps + CBlockIndex *pindex = ::ChainActive().FindEarliestAtLeast( + heightParam - TIMESTAMP_WINDOW, 0); + if (!pindex) { + throw JSONRPCError(RPC_INVALID_PARAMETER, + "Could not find block with at least the " + "specified timestamp."); + } + heightParam = pindex->nHeight; + } - unsigned int height = (unsigned int)heightParam; - unsigned int chainHeight = (unsigned int)::ChainActive().Height(); - if (chainHeight < config.GetChainParams().PruneAfterHeight()) { - throw JSONRPCError(RPC_MISC_ERROR, - "Blockchain is too short for pruning."); - } else if (height > chainHeight) { - throw JSONRPCError( - RPC_INVALID_PARAMETER, - "Blockchain is shorter than the attempted prune height."); - } else if (height > chainHeight - MIN_BLOCKS_TO_KEEP) { - LogPrint(BCLog::RPC, "Attempt to prune blocks close to the tip. " - "Retaining the minimum number of blocks.\n"); - height = chainHeight - MIN_BLOCKS_TO_KEEP; - } + unsigned int height = (unsigned int)heightParam; + unsigned int chainHeight = (unsigned int)::ChainActive().Height(); + if (chainHeight < config.GetChainParams().PruneAfterHeight()) { + throw JSONRPCError(RPC_MISC_ERROR, + "Blockchain is too short for pruning."); + } else if (height > chainHeight) { + throw JSONRPCError( + RPC_INVALID_PARAMETER, + "Blockchain is shorter than the attempted prune height."); + } else if (height > chainHeight - MIN_BLOCKS_TO_KEEP) { + LogPrint(BCLog::RPC, + "Attempt to prune blocks close to the tip. " + "Retaining the minimum number of blocks.\n"); + height = chainHeight - MIN_BLOCKS_TO_KEEP; + } - PruneBlockFilesManual(height); - const CBlockIndex *block = ::ChainActive().Tip(); - CHECK_NONFATAL(block); - while (block->pprev && (block->pprev->nStatus.hasData())) { - block = block->pprev; - } - return uint64_t(block->nHeight); + PruneBlockFilesManual(height); + const CBlockIndex *block = ::ChainActive().Tip(); + CHECK_NONFATAL(block); + while (block->pprev && (block->pprev->nStatus.hasData())) { + block = block->pprev; + } + return uint64_t(block->nHeight); + }, + }; } -static UniValue gettxoutsetinfo(const Config &config, - const JSONRPCRequest &request) { - RPCHelpMan{ +static RPCHelpMan gettxoutsetinfo() { + return RPCHelpMan{ "gettxoutsetinfo", "Returns statistics about the unspent transaction output set.\n" "Note this call may take some time.\n", @@ -1214,40 +1227,43 @@ }}, RPCExamples{HelpExampleCli("gettxoutsetinfo", "") + HelpExampleRpc("gettxoutsetinfo", "")}, - } - .Check(request); + [&](const RPCHelpMan &self, const Config &config, + const JSONRPCRequest &request) -> UniValue { + UniValue ret(UniValue::VOBJ); - UniValue ret(UniValue::VOBJ); + CCoinsStats stats; + ::ChainstateActive().ForceFlushStateToDisk(); - CCoinsStats stats; - ::ChainstateActive().ForceFlushStateToDisk(); - - const CoinStatsHashType hash_type = - ParseHashType(request.params[0], CoinStatsHashType::HASH_SERIALIZED); - - CCoinsView *coins_view = - WITH_LOCK(cs_main, return &ChainstateActive().CoinsDB()); - NodeContext &node = EnsureNodeContext(request.context); - if (GetUTXOStats(coins_view, stats, hash_type, - node.rpc_interruption_point)) { - ret.pushKV("height", int64_t(stats.nHeight)); - ret.pushKV("bestblock", stats.hashBlock.GetHex()); - ret.pushKV("transactions", int64_t(stats.nTransactions)); - ret.pushKV("txouts", int64_t(stats.nTransactionOutputs)); - ret.pushKV("bogosize", int64_t(stats.nBogoSize)); - if (hash_type == CoinStatsHashType::HASH_SERIALIZED) { - ret.pushKV("hash_serialized", stats.hashSerialized.GetHex()); - } - ret.pushKV("disk_size", stats.nDiskSize); - ret.pushKV("total_amount", stats.nTotalAmount); - } else { - throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set"); - } - return ret; + const CoinStatsHashType hash_type = ParseHashType( + request.params[0], CoinStatsHashType::HASH_SERIALIZED); + + CCoinsView *coins_view = + WITH_LOCK(cs_main, return &ChainstateActive().CoinsDB()); + NodeContext &node = EnsureNodeContext(request.context); + if (GetUTXOStats(coins_view, stats, hash_type, + node.rpc_interruption_point)) { + ret.pushKV("height", int64_t(stats.nHeight)); + ret.pushKV("bestblock", stats.hashBlock.GetHex()); + ret.pushKV("transactions", int64_t(stats.nTransactions)); + ret.pushKV("txouts", int64_t(stats.nTransactionOutputs)); + ret.pushKV("bogosize", int64_t(stats.nBogoSize)); + if (hash_type == CoinStatsHashType::HASH_SERIALIZED) { + ret.pushKV("hash_serialized", + stats.hashSerialized.GetHex()); + } + ret.pushKV("disk_size", stats.nDiskSize); + ret.pushKV("total_amount", stats.nTotalAmount); + } else { + throw JSONRPCError(RPC_INTERNAL_ERROR, + "Unable to read UTXO set"); + } + return ret; + }, + }; } -UniValue gettxout(const Config &config, const JSONRPCRequest &request) { - RPCHelpMan{ +RPCHelpMan gettxout() { + return RPCHelpMan{ "gettxout", "Returns details about an unspent transaction output.\n", { @@ -1291,57 +1307,58 @@ HelpExampleCli("gettxout", "\"txid\" 1") + "\nAs a JSON-RPC call\n" + HelpExampleRpc("gettxout", "\"txid\", 1")}, - } - .Check(request); - - LOCK(cs_main); - - UniValue ret(UniValue::VOBJ); + [&](const RPCHelpMan &self, const Config &config, + const JSONRPCRequest &request) -> UniValue { + LOCK(cs_main); - TxId txid(ParseHashV(request.params[0], "txid")); - int n = request.params[1].get_int(); - COutPoint out(txid, n); - bool fMempool = true; - if (!request.params[2].isNull()) { - fMempool = request.params[2].get_bool(); - } + UniValue ret(UniValue::VOBJ); - Coin coin; - CCoinsViewCache *coins_view = &::ChainstateActive().CoinsTip(); + TxId txid(ParseHashV(request.params[0], "txid")); + int n = request.params[1].get_int(); + COutPoint out(txid, n); + bool fMempool = true; + if (!request.params[2].isNull()) { + fMempool = request.params[2].get_bool(); + } - if (fMempool) { - const CTxMemPool &mempool = EnsureMemPool(request.context); - LOCK(mempool.cs); - CCoinsViewMemPool view(coins_view, mempool); - if (!view.GetCoin(out, coin) || mempool.isSpent(out)) { - return NullUniValue; - } - } else { - if (!coins_view->GetCoin(out, coin)) { - return NullUniValue; - } - } + Coin coin; + CCoinsViewCache *coins_view = &::ChainstateActive().CoinsTip(); + + if (fMempool) { + const CTxMemPool &mempool = EnsureMemPool(request.context); + LOCK(mempool.cs); + CCoinsViewMemPool view(coins_view, mempool); + if (!view.GetCoin(out, coin) || mempool.isSpent(out)) { + return NullUniValue; + } + } else { + if (!coins_view->GetCoin(out, coin)) { + return NullUniValue; + } + } - const CBlockIndex *pindex = LookupBlockIndex(coins_view->GetBestBlock()); - ret.pushKV("bestblock", pindex->GetBlockHash().GetHex()); - if (coin.GetHeight() == MEMPOOL_HEIGHT) { - ret.pushKV("confirmations", 0); - } else { - ret.pushKV("confirmations", - int64_t(pindex->nHeight - coin.GetHeight() + 1)); - } - ret.pushKV("value", coin.GetTxOut().nValue); - UniValue o(UniValue::VOBJ); - ScriptPubKeyToUniv(coin.GetTxOut().scriptPubKey, o, true); - ret.pushKV("scriptPubKey", o); - ret.pushKV("coinbase", coin.IsCoinBase()); + const CBlockIndex *pindex = + LookupBlockIndex(coins_view->GetBestBlock()); + ret.pushKV("bestblock", pindex->GetBlockHash().GetHex()); + if (coin.GetHeight() == MEMPOOL_HEIGHT) { + ret.pushKV("confirmations", 0); + } else { + ret.pushKV("confirmations", + int64_t(pindex->nHeight - coin.GetHeight() + 1)); + } + ret.pushKV("value", coin.GetTxOut().nValue); + UniValue o(UniValue::VOBJ); + ScriptPubKeyToUniv(coin.GetTxOut().scriptPubKey, o, true); + ret.pushKV("scriptPubKey", o); + ret.pushKV("coinbase", coin.IsCoinBase()); - return ret; + return ret; + }, + }; } -static UniValue verifychain(const Config &config, - const JSONRPCRequest &request) { - RPCHelpMan{ +static RPCHelpMan verifychain() { + return RPCHelpMan{ "verifychain", "Verifies blockchain database.\n", { @@ -1356,20 +1373,22 @@ RPCResult{RPCResult::Type::BOOL, "", "Verified or not"}, RPCExamples{HelpExampleCli("verifychain", "") + HelpExampleRpc("verifychain", "")}, - } - .Check(request); - - const int check_level(request.params[0].isNull() - ? DEFAULT_CHECKLEVEL - : request.params[0].get_int()); - const int check_depth{request.params[1].isNull() - ? DEFAULT_CHECKBLOCKS - : request.params[1].get_int()}; + [&](const RPCHelpMan &self, const Config &config, + const JSONRPCRequest &request) -> UniValue { + const int check_level(request.params[0].isNull() + ? DEFAULT_CHECKLEVEL + : request.params[0].get_int()); + const int check_depth{request.params[1].isNull() + ? DEFAULT_CHECKBLOCKS + : request.params[1].get_int()}; - LOCK(cs_main); + LOCK(cs_main); - return CVerifyDB().VerifyDB(config, &::ChainstateActive().CoinsTip(), - check_level, check_depth); + return CVerifyDB().VerifyDB(config, + &::ChainstateActive().CoinsTip(), + check_level, check_depth); + }, + }; } static void BIP9SoftForkDescPushBack(UniValue &softforks, @@ -1434,9 +1453,8 @@ softforks.pushKV(VersionBitsDeploymentInfo[id].name, rv); } -UniValue getblockchaininfo(const Config &config, - const JSONRPCRequest &request) { - RPCHelpMan{ +RPCHelpMan getblockchaininfo() { + return RPCHelpMan{ "getblockchaininfo", "Returns an object containing various state info regarding blockchain " "processing.\n", @@ -1547,55 +1565,58 @@ }}, RPCExamples{HelpExampleCli("getblockchaininfo", "") + HelpExampleRpc("getblockchaininfo", "")}, - } - .Check(request); - - LOCK(cs_main); - - const CChainParams &chainparams = config.GetChainParams(); - - const CBlockIndex *tip = ::ChainActive().Tip(); - UniValue obj(UniValue::VOBJ); - obj.pushKV("chain", chainparams.NetworkIDString()); - obj.pushKV("blocks", int(::ChainActive().Height())); - obj.pushKV("headers", pindexBestHeader ? pindexBestHeader->nHeight : -1); - obj.pushKV("bestblockhash", tip->GetBlockHash().GetHex()); - obj.pushKV("difficulty", double(GetDifficulty(tip))); - obj.pushKV("mediantime", int64_t(tip->GetMedianTimePast())); - obj.pushKV("verificationprogress", - GuessVerificationProgress(Params().TxData(), tip)); - obj.pushKV("initialblockdownload", - ::ChainstateActive().IsInitialBlockDownload()); - obj.pushKV("chainwork", tip->nChainWork.GetHex()); - obj.pushKV("size_on_disk", CalculateCurrentUsage()); - obj.pushKV("pruned", fPruneMode); - - if (fPruneMode) { - const CBlockIndex *block = tip; - CHECK_NONFATAL(block); - while (block->pprev && (block->pprev->nStatus.hasData())) { - block = block->pprev; - } - - obj.pushKV("pruneheight", block->nHeight); + [&](const RPCHelpMan &self, const Config &config, + const JSONRPCRequest &request) -> UniValue { + LOCK(cs_main); - // if 0, execution bypasses the whole if block. - bool automatic_pruning = (gArgs.GetArg("-prune", 0) != 1); - obj.pushKV("automatic_pruning", automatic_pruning); - if (automatic_pruning) { - obj.pushKV("prune_target_size", nPruneTarget); - } - } + const CChainParams &chainparams = config.GetChainParams(); + + const CBlockIndex *tip = ::ChainActive().Tip(); + UniValue obj(UniValue::VOBJ); + obj.pushKV("chain", chainparams.NetworkIDString()); + obj.pushKV("blocks", int(::ChainActive().Height())); + obj.pushKV("headers", + pindexBestHeader ? pindexBestHeader->nHeight : -1); + obj.pushKV("bestblockhash", tip->GetBlockHash().GetHex()); + obj.pushKV("difficulty", double(GetDifficulty(tip))); + obj.pushKV("mediantime", int64_t(tip->GetMedianTimePast())); + obj.pushKV("verificationprogress", + GuessVerificationProgress(Params().TxData(), tip)); + obj.pushKV("initialblockdownload", + ::ChainstateActive().IsInitialBlockDownload()); + obj.pushKV("chainwork", tip->nChainWork.GetHex()); + obj.pushKV("size_on_disk", CalculateCurrentUsage()); + obj.pushKV("pruned", fPruneMode); + + if (fPruneMode) { + const CBlockIndex *block = tip; + CHECK_NONFATAL(block); + while (block->pprev && (block->pprev->nStatus.hasData())) { + block = block->pprev; + } + + obj.pushKV("pruneheight", block->nHeight); + + // if 0, execution bypasses the whole if block. + bool automatic_pruning = (gArgs.GetArg("-prune", 0) != 1); + obj.pushKV("automatic_pruning", automatic_pruning); + if (automatic_pruning) { + obj.pushKV("prune_target_size", nPruneTarget); + } + } - UniValue softforks(UniValue::VOBJ); - for (int i = 0; i < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; i++) { - BIP9SoftForkDescPushBack(softforks, chainparams.GetConsensus(), - Consensus::DeploymentPos(i)); - } - obj.pushKV("softforks", softforks); + UniValue softforks(UniValue::VOBJ); + for (int i = 0; i < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; + i++) { + BIP9SoftForkDescPushBack(softforks, chainparams.GetConsensus(), + Consensus::DeploymentPos(i)); + } + obj.pushKV("softforks", softforks); - obj.pushKV("warnings", GetWarnings(false).original); - return obj; + obj.pushKV("warnings", GetWarnings(false).original); + return obj; + }, + }; } /** Comparison function for sorting the getchaintips heads. */ @@ -1611,9 +1632,8 @@ } }; -static UniValue getchaintips(const Config &config, - const JSONRPCRequest &request) { - RPCHelpMan{ +static RPCHelpMan getchaintips() { + return RPCHelpMan{ "getchaintips", "Return information about all known tips in the block tree, including " "the main chain as well as orphaned branches.\n", @@ -1649,87 +1669,93 @@ }}}}, RPCExamples{HelpExampleCli("getchaintips", "") + HelpExampleRpc("getchaintips", "")}, - } - .Check(request); - - ChainstateManager &chainman = EnsureChainman(request.context); - LOCK(cs_main); - - /** - * Idea: The set of chain tips is the active chain tip, plus orphan blocks - * which do not have another orphan building off of them. Algorithm: - * - Make one pass through BlockIndex(), picking out the orphan - * blocks, and also storing a set of the orphan block's pprev pointers. - * - Iterate through the orphan blocks. If the block isn't pointed to by - * another orphan, it is a chain tip. - * - Add the active chain tip - */ - std::set setTips; - std::set setOrphans; - std::set setPrevs; - - for (const std::pair &item : - chainman.BlockIndex()) { - if (!chainman.ActiveChain().Contains(item.second)) { - setOrphans.insert(item.second); - setPrevs.insert(item.second->pprev); - } - } + [&](const RPCHelpMan &self, const Config &config, + const JSONRPCRequest &request) -> UniValue { + ChainstateManager &chainman = EnsureChainman(request.context); + LOCK(cs_main); - for (std::set::iterator it = setOrphans.begin(); - it != setOrphans.end(); ++it) { - if (setPrevs.erase(*it) == 0) { - setTips.insert(*it); - } - } + /** + * Idea: The set of chain tips is the active chain tip, plus orphan + * blocks which do not have another orphan building off of them. + * Algorithm: + * - Make one pass through BlockIndex(), picking out the orphan + * blocks, and also storing a set of the orphan block's pprev + * pointers. + * - Iterate through the orphan blocks. If the block isn't pointed + * to by another orphan, it is a chain tip. + * - Add the active chain tip + */ + std::set setTips; + std::set setOrphans; + std::set setPrevs; + + for (const std::pair &item : + chainman.BlockIndex()) { + if (!chainman.ActiveChain().Contains(item.second)) { + setOrphans.insert(item.second); + setPrevs.insert(item.second->pprev); + } + } - // Always report the currently active tip. - setTips.insert(chainman.ActiveChain().Tip()); - - /* Construct the output array. */ - UniValue res(UniValue::VARR); - for (const CBlockIndex *block : setTips) { - UniValue obj(UniValue::VOBJ); - obj.pushKV("height", block->nHeight); - obj.pushKV("hash", block->phashBlock->GetHex()); - - const int branchLen = - block->nHeight - chainman.ActiveChain().FindFork(block)->nHeight; - obj.pushKV("branchlen", branchLen); - - std::string status; - if (chainman.ActiveChain().Contains(block)) { - // This block is part of the currently active chain. - status = "active"; - } else if (block->nStatus.isInvalid()) { - // This block or one of its ancestors is invalid. - status = "invalid"; - } else if (block->nStatus.isOnParkedChain()) { - // This block or one of its ancestors is parked. - status = "parked"; - } else if (!block->HaveTxsDownloaded()) { - // This block cannot be connected because full block data for it or - // one of its parents is missing. - status = "headers-only"; - } else if (block->IsValid(BlockValidity::SCRIPTS)) { - // This block is fully validated, but no longer part of the active - // chain. It was probably the active block once, but was - // reorganized. - status = "valid-fork"; - } else if (block->IsValid(BlockValidity::TREE)) { - // The headers for this block are valid, but it has not been - // validated. It was probably never part of the most-work chain. - status = "valid-headers"; - } else { - // No clue. - status = "unknown"; - } - obj.pushKV("status", status); + for (std::set::iterator it = + setOrphans.begin(); + it != setOrphans.end(); ++it) { + if (setPrevs.erase(*it) == 0) { + setTips.insert(*it); + } + } - res.push_back(obj); - } + // Always report the currently active tip. + setTips.insert(chainman.ActiveChain().Tip()); + + /* Construct the output array. */ + UniValue res(UniValue::VARR); + for (const CBlockIndex *block : setTips) { + UniValue obj(UniValue::VOBJ); + obj.pushKV("height", block->nHeight); + obj.pushKV("hash", block->phashBlock->GetHex()); + + const int branchLen = + block->nHeight - + chainman.ActiveChain().FindFork(block)->nHeight; + obj.pushKV("branchlen", branchLen); + + std::string status; + if (chainman.ActiveChain().Contains(block)) { + // This block is part of the currently active chain. + status = "active"; + } else if (block->nStatus.isInvalid()) { + // This block or one of its ancestors is invalid. + status = "invalid"; + } else if (block->nStatus.isOnParkedChain()) { + // This block or one of its ancestors is parked. + status = "parked"; + } else if (!block->HaveTxsDownloaded()) { + // This block cannot be connected because full block data + // for it or one of its parents is missing. + status = "headers-only"; + } else if (block->IsValid(BlockValidity::SCRIPTS)) { + // This block is fully validated, but no longer part of the + // active chain. It was probably the active block once, but + // was reorganized. + status = "valid-fork"; + } else if (block->IsValid(BlockValidity::TREE)) { + // The headers for this block are valid, but it has not been + // validated. It was probably never part of the most-work + // chain. + status = "valid-headers"; + } else { + // No clue. + status = "unknown"; + } + obj.pushKV("status", status); + + res.push_back(obj); + } - return res; + return res; + }, + }; } UniValue MempoolInfoToJSON(const CTxMemPool &pool) { @@ -1751,9 +1777,8 @@ return ret; } -static UniValue getmempoolinfo(const Config &config, - const JSONRPCRequest &request) { - RPCHelpMan{ +static RPCHelpMan getmempoolinfo() { + return RPCHelpMan{ "getmempoolinfo", "Returns details on the active state of the TX memory pool.\n", {}, @@ -1782,15 +1807,15 @@ }}, RPCExamples{HelpExampleCli("getmempoolinfo", "") + HelpExampleRpc("getmempoolinfo", "")}, - } - .Check(request); - - return MempoolInfoToJSON(EnsureMemPool(request.context)); + [&](const RPCHelpMan &self, const Config &config, + const JSONRPCRequest &request) -> UniValue { + return MempoolInfoToJSON(EnsureMemPool(request.context)); + }, + }; } -static UniValue preciousblock(const Config &config, - const JSONRPCRequest &request) { - RPCHelpMan{ +static RPCHelpMan preciousblock() { + return RPCHelpMan{ "preciousblock", "Treats a block as if it were received before others with the same " "work.\n" @@ -1804,32 +1829,34 @@ RPCResult{RPCResult::Type::NONE, "", ""}, RPCExamples{HelpExampleCli("preciousblock", "\"blockhash\"") + HelpExampleRpc("preciousblock", "\"blockhash\"")}, - } - .Check(request); - - BlockHash hash(ParseHashV(request.params[0], "blockhash")); - CBlockIndex *pblockindex; + [&](const RPCHelpMan &self, const Config &config, + const JSONRPCRequest &request) -> UniValue { + BlockHash hash(ParseHashV(request.params[0], "blockhash")); + CBlockIndex *pblockindex; - { - LOCK(cs_main); - pblockindex = LookupBlockIndex(hash); - if (!pblockindex) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); - } - } + { + LOCK(cs_main); + pblockindex = LookupBlockIndex(hash); + if (!pblockindex) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, + "Block not found"); + } + } - BlockValidationState state; - PreciousBlock(config, state, pblockindex); + BlockValidationState state; + PreciousBlock(config, state, pblockindex); - if (!state.IsValid()) { - throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason()); - } + if (!state.IsValid()) { + throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason()); + } - return NullUniValue; + return NullUniValue; + }, + }; } -UniValue finalizeblock(const Config &config, const JSONRPCRequest &request) { - RPCHelpMan{ +RPCHelpMan finalizeblock() { + return RPCHelpMan{ "finalizeblock", "Treats a block as final. It cannot be reorged. Any chain\n" "that does not contain this block is invalid. Used on a less\n" @@ -1842,38 +1869,39 @@ RPCResult{RPCResult::Type::NONE, "", ""}, RPCExamples{HelpExampleCli("finalizeblock", "\"blockhash\"") + HelpExampleRpc("finalizeblock", "\"blockhash\"")}, - } - .Check(request); - - std::string strHash = request.params[0].get_str(); - BlockHash hash(uint256S(strHash)); - BlockValidationState state; - - CBlockIndex *pblockindex = nullptr; - { - LOCK(cs_main); - pblockindex = LookupBlockIndex(hash); - if (!pblockindex) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); - } - } // end of locked cs_main scope + [&](const RPCHelpMan &self, const Config &config, + const JSONRPCRequest &request) -> UniValue { + std::string strHash = request.params[0].get_str(); + BlockHash hash(uint256S(strHash)); + BlockValidationState state; - ::ChainstateActive().FinalizeBlock(config, state, pblockindex); - - if (state.IsValid()) { - ActivateBestChain(config, state); - } + CBlockIndex *pblockindex = nullptr; + { + LOCK(cs_main); + pblockindex = LookupBlockIndex(hash); + if (!pblockindex) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, + "Block not found"); + } + } // end of locked cs_main scope + + ::ChainstateActive().FinalizeBlock(config, state, pblockindex); + + if (state.IsValid()) { + ActivateBestChain(config, state); + } - if (!state.IsValid()) { - throw JSONRPCError(RPC_DATABASE_ERROR, state.ToString()); - } + if (!state.IsValid()) { + throw JSONRPCError(RPC_DATABASE_ERROR, state.ToString()); + } - return NullUniValue; + return NullUniValue; + }, + }; } -static UniValue invalidateblock(const Config &config, - const JSONRPCRequest &request) { - RPCHelpMan{ +static RPCHelpMan invalidateblock() { + return RPCHelpMan{ "invalidateblock", "Permanently marks a block as invalid, as if it violated a consensus " "rule.\n", @@ -1884,35 +1912,37 @@ RPCResult{RPCResult::Type::NONE, "", ""}, RPCExamples{HelpExampleCli("invalidateblock", "\"blockhash\"") + HelpExampleRpc("invalidateblock", "\"blockhash\"")}, - } - .Check(request); - - const BlockHash hash(ParseHashV(request.params[0], "blockhash")); - BlockValidationState state; + [&](const RPCHelpMan &self, const Config &config, + const JSONRPCRequest &request) -> UniValue { + const BlockHash hash(ParseHashV(request.params[0], "blockhash")); + BlockValidationState state; - CBlockIndex *pblockindex; - { - LOCK(cs_main); - pblockindex = LookupBlockIndex(hash); - if (!pblockindex) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); - } - } - ::ChainstateActive().InvalidateBlock(config, state, pblockindex); + CBlockIndex *pblockindex; + { + LOCK(cs_main); + pblockindex = LookupBlockIndex(hash); + if (!pblockindex) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, + "Block not found"); + } + } + ::ChainstateActive().InvalidateBlock(config, state, pblockindex); - if (state.IsValid()) { - ActivateBestChain(config, state); - } + if (state.IsValid()) { + ActivateBestChain(config, state); + } - if (!state.IsValid()) { - throw JSONRPCError(RPC_DATABASE_ERROR, state.ToString()); - } + if (!state.IsValid()) { + throw JSONRPCError(RPC_DATABASE_ERROR, state.ToString()); + } - return NullUniValue; + return NullUniValue; + }, + }; } -UniValue parkblock(const Config &config, const JSONRPCRequest &request) { - RPCHelpMan{ +RPCHelpMan parkblock() { + return RPCHelpMan{ "parkblock", "Marks a block as parked.\n", { @@ -1922,37 +1952,38 @@ RPCResult{RPCResult::Type::NONE, "", ""}, RPCExamples{HelpExampleCli("parkblock", "\"blockhash\"") + HelpExampleRpc("parkblock", "\"blockhash\"")}, - } - .Check(request); - - const std::string strHash = request.params[0].get_str(); - const BlockHash hash(uint256S(strHash)); - BlockValidationState state; - - CBlockIndex *pblockindex = nullptr; - { - LOCK(cs_main); - pblockindex = LookupBlockIndex(hash); - if (!pblockindex) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); - } - } - ::ChainstateActive().ParkBlock(config, state, pblockindex); + [&](const RPCHelpMan &self, const Config &config, + const JSONRPCRequest &request) -> UniValue { + const std::string strHash = request.params[0].get_str(); + const BlockHash hash(uint256S(strHash)); + BlockValidationState state; - if (state.IsValid()) { - ActivateBestChain(config, state); - } + CBlockIndex *pblockindex = nullptr; + { + LOCK(cs_main); + pblockindex = LookupBlockIndex(hash); + if (!pblockindex) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, + "Block not found"); + } + } + ::ChainstateActive().ParkBlock(config, state, pblockindex); - if (!state.IsValid()) { - throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason()); - } + if (state.IsValid()) { + ActivateBestChain(config, state); + } - return NullUniValue; + if (!state.IsValid()) { + throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason()); + } + + return NullUniValue; + }, + }; } -static UniValue reconsiderblock(const Config &config, - const JSONRPCRequest &request) { - RPCHelpMan{ +static RPCHelpMan reconsiderblock() { + return RPCHelpMan{ "reconsiderblock", "Removes invalidity status of a block, its ancestors and its" "descendants, reconsider them for activation.\n" @@ -1964,33 +1995,35 @@ RPCResult{RPCResult::Type::NONE, "", ""}, RPCExamples{HelpExampleCli("reconsiderblock", "\"blockhash\"") + HelpExampleRpc("reconsiderblock", "\"blockhash\"")}, - } - .Check(request); + [&](const RPCHelpMan &self, const Config &config, + const JSONRPCRequest &request) -> UniValue { + const BlockHash hash(ParseHashV(request.params[0], "blockhash")); - const BlockHash hash(ParseHashV(request.params[0], "blockhash")); - - { - LOCK(cs_main); - CBlockIndex *pblockindex = LookupBlockIndex(hash); - if (!pblockindex) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); - } - - ResetBlockFailureFlags(pblockindex); - } + { + LOCK(cs_main); + CBlockIndex *pblockindex = LookupBlockIndex(hash); + if (!pblockindex) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, + "Block not found"); + } + + ResetBlockFailureFlags(pblockindex); + } - BlockValidationState state; - ActivateBestChain(config, state); + BlockValidationState state; + ActivateBestChain(config, state); - if (!state.IsValid()) { - throw JSONRPCError(RPC_DATABASE_ERROR, state.ToString()); - } + if (!state.IsValid()) { + throw JSONRPCError(RPC_DATABASE_ERROR, state.ToString()); + } - return NullUniValue; + return NullUniValue; + }, + }; } -UniValue unparkblock(const Config &config, const JSONRPCRequest &request) { - RPCHelpMan{ +RPCHelpMan unparkblock() { + return RPCHelpMan{ "unparkblock", "Removes parked status of a block and its descendants, reconsider " "them for activation.\n" @@ -2002,36 +2035,37 @@ RPCResult{RPCResult::Type::NONE, "", ""}, RPCExamples{HelpExampleCli("unparkblock", "\"blockhash\"") + HelpExampleRpc("unparkblock", "\"blockhash\"")}, - } - .Check(request); - - const std::string strHash = request.params[0].get_str(); - const BlockHash hash(uint256S(strHash)); + [&](const RPCHelpMan &self, const Config &config, + const JSONRPCRequest &request) -> UniValue { + const std::string strHash = request.params[0].get_str(); + const BlockHash hash(uint256S(strHash)); - { - LOCK(cs_main); + { + LOCK(cs_main); - CBlockIndex *pblockindex = LookupBlockIndex(hash); - if (!pblockindex) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); - } + CBlockIndex *pblockindex = LookupBlockIndex(hash); + if (!pblockindex) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, + "Block not found"); + } - UnparkBlockAndChildren(pblockindex); - } + UnparkBlockAndChildren(pblockindex); + } - BlockValidationState state; - ActivateBestChain(config, state); + BlockValidationState state; + ActivateBestChain(config, state); - if (!state.IsValid()) { - throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason()); - } + if (!state.IsValid()) { + throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason()); + } - return NullUniValue; + return NullUniValue; + }, + }; } -static UniValue getchaintxstats(const Config &config, - const JSONRPCRequest &request) { - RPCHelpMan{ +static RPCHelpMan getchaintxstats() { + return RPCHelpMan{ "getchaintxstats", "Compute statistics about the total number and rate of transactions " "in the chain.\n", @@ -2070,67 +2104,74 @@ }}, RPCExamples{HelpExampleCli("getchaintxstats", "") + HelpExampleRpc("getchaintxstats", "2016")}, - } - .Check(request); - - const CBlockIndex *pindex; - - // By default: 1 month - int blockcount = 30 * 24 * 60 * 60 / - config.GetChainParams().GetConsensus().nPowTargetSpacing; - - if (request.params[1].isNull()) { - LOCK(cs_main); - pindex = ::ChainActive().Tip(); - } else { - BlockHash hash(ParseHashV(request.params[1], "blockhash")); - LOCK(cs_main); - pindex = LookupBlockIndex(hash); - if (!pindex) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); - } - if (!::ChainActive().Contains(pindex)) { - throw JSONRPCError(RPC_INVALID_PARAMETER, - "Block is not in main chain"); - } - } - - CHECK_NONFATAL(pindex != nullptr); - - if (request.params[0].isNull()) { - blockcount = std::max(0, std::min(blockcount, pindex->nHeight - 1)); - } else { - blockcount = request.params[0].get_int(); - - if (blockcount < 0 || - (blockcount > 0 && blockcount >= pindex->nHeight)) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block count: " - "should be between 0 and " - "the block's height - 1"); - } - } + [&](const RPCHelpMan &self, const Config &config, + const JSONRPCRequest &request) -> UniValue { + const CBlockIndex *pindex; + + // By default: 1 month + int blockcount = + 30 * 24 * 60 * 60 / + config.GetChainParams().GetConsensus().nPowTargetSpacing; + + if (request.params[1].isNull()) { + LOCK(cs_main); + pindex = ::ChainActive().Tip(); + } else { + BlockHash hash(ParseHashV(request.params[1], "blockhash")); + LOCK(cs_main); + pindex = LookupBlockIndex(hash); + if (!pindex) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, + "Block not found"); + } + if (!::ChainActive().Contains(pindex)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, + "Block is not in main chain"); + } + } - const CBlockIndex *pindexPast = - pindex->GetAncestor(pindex->nHeight - blockcount); - int nTimeDiff = - pindex->GetMedianTimePast() - pindexPast->GetMedianTimePast(); - int nTxDiff = pindex->GetChainTxCount() - pindexPast->GetChainTxCount(); + CHECK_NONFATAL(pindex != nullptr); + + if (request.params[0].isNull()) { + blockcount = + std::max(0, std::min(blockcount, pindex->nHeight - 1)); + } else { + blockcount = request.params[0].get_int(); + + if (blockcount < 0 || + (blockcount > 0 && blockcount >= pindex->nHeight)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, + "Invalid block count: " + "should be between 0 and " + "the block's height - 1"); + } + } - UniValue ret(UniValue::VOBJ); - ret.pushKV("time", pindex->GetBlockTime()); - ret.pushKV("txcount", pindex->GetChainTxCount()); - ret.pushKV("window_final_block_hash", pindex->GetBlockHash().GetHex()); - ret.pushKV("window_final_block_height", pindex->nHeight); - ret.pushKV("window_block_count", blockcount); - if (blockcount > 0) { - ret.pushKV("window_tx_count", nTxDiff); - ret.pushKV("window_interval", nTimeDiff); - if (nTimeDiff > 0) { - ret.pushKV("txrate", double(nTxDiff) / nTimeDiff); - } - } + const CBlockIndex *pindexPast = + pindex->GetAncestor(pindex->nHeight - blockcount); + int nTimeDiff = + pindex->GetMedianTimePast() - pindexPast->GetMedianTimePast(); + int nTxDiff = + pindex->GetChainTxCount() - pindexPast->GetChainTxCount(); + + UniValue ret(UniValue::VOBJ); + ret.pushKV("time", pindex->GetBlockTime()); + ret.pushKV("txcount", pindex->GetChainTxCount()); + ret.pushKV("window_final_block_hash", + pindex->GetBlockHash().GetHex()); + ret.pushKV("window_final_block_height", pindex->nHeight); + ret.pushKV("window_block_count", blockcount); + if (blockcount > 0) { + ret.pushKV("window_tx_count", nTxDiff); + ret.pushKV("window_interval", nTimeDiff); + if (nTimeDiff > 0) { + ret.pushKV("txrate", double(nTxDiff) / nTimeDiff); + } + } - return ret; + return ret; + }, + }; } template @@ -2161,10 +2202,9 @@ static constexpr size_t PER_UTXO_OVERHEAD = sizeof(COutPoint) + sizeof(uint32_t) + sizeof(bool); -static UniValue getblockstats(const Config &config, - const JSONRPCRequest &request) { +static RPCHelpMan getblockstats() { const auto &ticker = Currency::get().ticker; - RPCHelpMan{ + return RPCHelpMan{ "getblockstats", "Compute per block statistics for a given window. All amounts are " "in " + @@ -2248,202 +2288,219 @@ R"("00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09", ["minfeerate","avgfeerate"])") + HelpExampleRpc("getblockstats", R"(1000, ["minfeerate","avgfeerate"])")}, - } - .Check(request); - - LOCK(cs_main); - - CBlockIndex *pindex; - if (request.params[0].isNum()) { - const int height = request.params[0].get_int(); - const int current_tip = ::ChainActive().Height(); - if (height < 0) { - throw JSONRPCError( - RPC_INVALID_PARAMETER, - strprintf("Target block height %d is negative", height)); - } - if (height > current_tip) { - throw JSONRPCError( - RPC_INVALID_PARAMETER, - strprintf("Target block height %d after current tip %d", height, - current_tip)); - } - - pindex = ::ChainActive()[height]; - } else { - const BlockHash hash(ParseHashV(request.params[0], "hash_or_height")); - pindex = LookupBlockIndex(hash); - if (!pindex) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); - } - if (!::ChainActive().Contains(pindex)) { - throw JSONRPCError(RPC_INVALID_PARAMETER, - strprintf("Block is not in chain %s", - Params().NetworkIDString())); - } - } - - CHECK_NONFATAL(pindex != nullptr); - - std::set stats; - if (!request.params[1].isNull()) { - const UniValue stats_univalue = request.params[1].get_array(); - for (unsigned int i = 0; i < stats_univalue.size(); i++) { - const std::string stat = stats_univalue[i].get_str(); - stats.insert(stat); - } - } + [&](const RPCHelpMan &self, const Config &config, + const JSONRPCRequest &request) -> UniValue { + LOCK(cs_main); - const CBlock block = GetBlockChecked(config, pindex); - const CBlockUndo blockUndo = GetUndoChecked(pindex); - - // Calculate everything if nothing selected (default) - const bool do_all = stats.size() == 0; - const bool do_mediantxsize = do_all || stats.count("mediantxsize") != 0; - const bool do_medianfee = do_all || stats.count("medianfee") != 0; - const bool do_medianfeerate = do_all || stats.count("medianfeerate") != 0; - const bool loop_inputs = - do_all || do_medianfee || do_medianfeerate || - SetHasKeys(stats, "utxo_size_inc", "totalfee", "avgfee", "avgfeerate", - "minfee", "maxfee", "minfeerate", "maxfeerate"); - const bool loop_outputs = do_all || loop_inputs || stats.count("total_out"); - const bool do_calculate_size = - do_mediantxsize || loop_inputs || - SetHasKeys(stats, "total_size", "avgtxsize", "mintxsize", "maxtxsize"); - - const int64_t blockMaxSize = config.GetMaxBlockSize(); - Amount maxfee = Amount::zero(); - Amount maxfeerate = Amount::zero(); - Amount minfee = MAX_MONEY; - Amount minfeerate = MAX_MONEY; - Amount total_out = Amount::zero(); - Amount totalfee = Amount::zero(); - int64_t inputs = 0; - int64_t maxtxsize = 0; - int64_t mintxsize = blockMaxSize; - int64_t outputs = 0; - int64_t total_size = 0; - int64_t utxo_size_inc = 0; - std::vector fee_array; - std::vector feerate_array; - std::vector txsize_array; - - for (size_t i = 0; i < block.vtx.size(); ++i) { - const auto &tx = block.vtx.at(i); - outputs += tx->vout.size(); - Amount tx_total_out = Amount::zero(); - if (loop_outputs) { - for (const CTxOut &out : tx->vout) { - tx_total_out += out.nValue; - utxo_size_inc += - GetSerializeSize(out, PROTOCOL_VERSION) + PER_UTXO_OVERHEAD; + CBlockIndex *pindex; + if (request.params[0].isNum()) { + const int height = request.params[0].get_int(); + const int current_tip = ::ChainActive().Height(); + if (height < 0) { + throw JSONRPCError( + RPC_INVALID_PARAMETER, + strprintf("Target block height %d is negative", + height)); + } + if (height > current_tip) { + throw JSONRPCError( + RPC_INVALID_PARAMETER, + strprintf("Target block height %d after current tip %d", + height, current_tip)); + } + + pindex = ::ChainActive()[height]; + } else { + const BlockHash hash( + ParseHashV(request.params[0], "hash_or_height")); + pindex = LookupBlockIndex(hash); + if (!pindex) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, + "Block not found"); + } + if (!::ChainActive().Contains(pindex)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, + strprintf("Block is not in chain %s", + Params().NetworkIDString())); + } } - } - - if (tx->IsCoinBase()) { - continue; - } - // Don't count coinbase's fake input - inputs += tx->vin.size(); - // Don't count coinbase reward - total_out += tx_total_out; + CHECK_NONFATAL(pindex != nullptr); - int64_t tx_size = 0; - if (do_calculate_size) { - tx_size = tx->GetTotalSize(); - if (do_mediantxsize) { - txsize_array.push_back(tx_size); + std::set stats; + if (!request.params[1].isNull()) { + const UniValue stats_univalue = request.params[1].get_array(); + for (unsigned int i = 0; i < stats_univalue.size(); i++) { + const std::string stat = stats_univalue[i].get_str(); + stats.insert(stat); + } } - maxtxsize = std::max(maxtxsize, tx_size); - mintxsize = std::min(mintxsize, tx_size); - total_size += tx_size; - } - - if (loop_inputs) { - Amount tx_total_in = Amount::zero(); - const auto &txundo = blockUndo.vtxundo.at(i - 1); - for (const Coin &coin : txundo.vprevout) { - const CTxOut &prevoutput = coin.GetTxOut(); - tx_total_in += prevoutput.nValue; - utxo_size_inc -= - GetSerializeSize(prevoutput, PROTOCOL_VERSION) + - PER_UTXO_OVERHEAD; + const CBlock block = GetBlockChecked(config, pindex); + const CBlockUndo blockUndo = GetUndoChecked(pindex); + + // Calculate everything if nothing selected (default) + const bool do_all = stats.size() == 0; + const bool do_mediantxsize = + do_all || stats.count("mediantxsize") != 0; + const bool do_medianfee = do_all || stats.count("medianfee") != 0; + const bool do_medianfeerate = + do_all || stats.count("medianfeerate") != 0; + const bool loop_inputs = + do_all || do_medianfee || do_medianfeerate || + SetHasKeys(stats, "utxo_size_inc", "totalfee", "avgfee", + "avgfeerate", "minfee", "maxfee", "minfeerate", + "maxfeerate"); + const bool loop_outputs = + do_all || loop_inputs || stats.count("total_out"); + const bool do_calculate_size = + do_mediantxsize || loop_inputs || + SetHasKeys(stats, "total_size", "avgtxsize", "mintxsize", + "maxtxsize"); + + const int64_t blockMaxSize = config.GetMaxBlockSize(); + Amount maxfee = Amount::zero(); + Amount maxfeerate = Amount::zero(); + Amount minfee = MAX_MONEY; + Amount minfeerate = MAX_MONEY; + Amount total_out = Amount::zero(); + Amount totalfee = Amount::zero(); + int64_t inputs = 0; + int64_t maxtxsize = 0; + int64_t mintxsize = blockMaxSize; + int64_t outputs = 0; + int64_t total_size = 0; + int64_t utxo_size_inc = 0; + std::vector fee_array; + std::vector feerate_array; + std::vector txsize_array; + + for (size_t i = 0; i < block.vtx.size(); ++i) { + const auto &tx = block.vtx.at(i); + outputs += tx->vout.size(); + Amount tx_total_out = Amount::zero(); + if (loop_outputs) { + for (const CTxOut &out : tx->vout) { + tx_total_out += out.nValue; + utxo_size_inc += + GetSerializeSize(out, PROTOCOL_VERSION) + + PER_UTXO_OVERHEAD; + } + } + + if (tx->IsCoinBase()) { + continue; + } + + // Don't count coinbase's fake input + inputs += tx->vin.size(); + // Don't count coinbase reward + total_out += tx_total_out; + + int64_t tx_size = 0; + if (do_calculate_size) { + tx_size = tx->GetTotalSize(); + if (do_mediantxsize) { + txsize_array.push_back(tx_size); + } + maxtxsize = std::max(maxtxsize, tx_size); + mintxsize = std::min(mintxsize, tx_size); + total_size += tx_size; + } + + if (loop_inputs) { + Amount tx_total_in = Amount::zero(); + const auto &txundo = blockUndo.vtxundo.at(i - 1); + for (const Coin &coin : txundo.vprevout) { + const CTxOut &prevoutput = coin.GetTxOut(); + + tx_total_in += prevoutput.nValue; + utxo_size_inc -= + GetSerializeSize(prevoutput, PROTOCOL_VERSION) + + PER_UTXO_OVERHEAD; + } + + Amount txfee = tx_total_in - tx_total_out; + CHECK_NONFATAL(MoneyRange(txfee)); + if (do_medianfee) { + fee_array.push_back(txfee); + } + maxfee = std::max(maxfee, txfee); + minfee = std::min(minfee, txfee); + totalfee += txfee; + + Amount feerate = txfee / tx_size; + if (do_medianfeerate) { + feerate_array.push_back(feerate); + } + maxfeerate = std::max(maxfeerate, feerate); + minfeerate = std::min(minfeerate, feerate); + } } - Amount txfee = tx_total_in - tx_total_out; - CHECK_NONFATAL(MoneyRange(txfee)); - if (do_medianfee) { - fee_array.push_back(txfee); + UniValue ret_all(UniValue::VOBJ); + ret_all.pushKV("avgfee", + block.vtx.size() > 1 + ? (totalfee / int((block.vtx.size() - 1))) + : Amount::zero()); + ret_all.pushKV("avgfeerate", total_size > 0 + ? (totalfee / total_size) + : Amount::zero()); + ret_all.pushKV("avgtxsize", + (block.vtx.size() > 1) + ? total_size / (block.vtx.size() - 1) + : 0); + ret_all.pushKV("blockhash", pindex->GetBlockHash().GetHex()); + ret_all.pushKV("height", (int64_t)pindex->nHeight); + ret_all.pushKV("ins", inputs); + ret_all.pushKV("maxfee", maxfee); + ret_all.pushKV("maxfeerate", maxfeerate); + ret_all.pushKV("maxtxsize", maxtxsize); + ret_all.pushKV("medianfee", CalculateTruncatedMedian(fee_array)); + ret_all.pushKV("medianfeerate", + CalculateTruncatedMedian(feerate_array)); + ret_all.pushKV("mediantime", pindex->GetMedianTimePast()); + ret_all.pushKV("mediantxsize", + CalculateTruncatedMedian(txsize_array)); + ret_all.pushKV("minfee", + minfee == MAX_MONEY ? Amount::zero() : minfee); + ret_all.pushKV("minfeerate", minfeerate == MAX_MONEY + ? Amount::zero() + : minfeerate); + ret_all.pushKV("mintxsize", + mintxsize == blockMaxSize ? 0 : mintxsize); + ret_all.pushKV("outs", outputs); + ret_all.pushKV("subsidy", GetBlockSubsidy(pindex->nHeight, + Params().GetConsensus())); + ret_all.pushKV("time", pindex->GetBlockTime()); + ret_all.pushKV("total_out", total_out); + ret_all.pushKV("total_size", total_size); + ret_all.pushKV("totalfee", totalfee); + ret_all.pushKV("txs", (int64_t)block.vtx.size()); + ret_all.pushKV("utxo_increase", outputs - inputs); + ret_all.pushKV("utxo_size_inc", utxo_size_inc); + + if (do_all) { + return ret_all; } - maxfee = std::max(maxfee, txfee); - minfee = std::min(minfee, txfee); - totalfee += txfee; - Amount feerate = txfee / tx_size; - if (do_medianfeerate) { - feerate_array.push_back(feerate); + UniValue ret(UniValue::VOBJ); + for (const std::string &stat : stats) { + const UniValue &value = ret_all[stat]; + if (value.isNull()) { + throw JSONRPCError( + RPC_INVALID_PARAMETER, + strprintf("Invalid selected statistic %s", stat)); + } + ret.pushKV(stat, value); } - maxfeerate = std::max(maxfeerate, feerate); - minfeerate = std::min(minfeerate, feerate); - } - } - - UniValue ret_all(UniValue::VOBJ); - ret_all.pushKV("avgfee", block.vtx.size() > 1 - ? (totalfee / int((block.vtx.size() - 1))) - : Amount::zero()); - ret_all.pushKV("avgfeerate", - total_size > 0 ? (totalfee / total_size) : Amount::zero()); - ret_all.pushKV("avgtxsize", (block.vtx.size() > 1) - ? total_size / (block.vtx.size() - 1) - : 0); - ret_all.pushKV("blockhash", pindex->GetBlockHash().GetHex()); - ret_all.pushKV("height", (int64_t)pindex->nHeight); - ret_all.pushKV("ins", inputs); - ret_all.pushKV("maxfee", maxfee); - ret_all.pushKV("maxfeerate", maxfeerate); - ret_all.pushKV("maxtxsize", maxtxsize); - ret_all.pushKV("medianfee", CalculateTruncatedMedian(fee_array)); - ret_all.pushKV("medianfeerate", CalculateTruncatedMedian(feerate_array)); - ret_all.pushKV("mediantime", pindex->GetMedianTimePast()); - ret_all.pushKV("mediantxsize", CalculateTruncatedMedian(txsize_array)); - ret_all.pushKV("minfee", minfee == MAX_MONEY ? Amount::zero() : minfee); - ret_all.pushKV("minfeerate", - minfeerate == MAX_MONEY ? Amount::zero() : minfeerate); - ret_all.pushKV("mintxsize", mintxsize == blockMaxSize ? 0 : mintxsize); - ret_all.pushKV("outs", outputs); - ret_all.pushKV("subsidy", - GetBlockSubsidy(pindex->nHeight, Params().GetConsensus())); - ret_all.pushKV("time", pindex->GetBlockTime()); - ret_all.pushKV("total_out", total_out); - ret_all.pushKV("total_size", total_size); - ret_all.pushKV("totalfee", totalfee); - ret_all.pushKV("txs", (int64_t)block.vtx.size()); - ret_all.pushKV("utxo_increase", outputs - inputs); - ret_all.pushKV("utxo_size_inc", utxo_size_inc); - - if (do_all) { - return ret_all; - } - - UniValue ret(UniValue::VOBJ); - for (const std::string &stat : stats) { - const UniValue &value = ret_all[stat]; - if (value.isNull()) { - throw JSONRPCError( - RPC_INVALID_PARAMETER, - strprintf("Invalid selected statistic %s", stat)); - } - ret.pushKV(stat, value); - } - return ret; + return ret; + }, + }; } -static UniValue savemempool(const Config &config, - const JSONRPCRequest &request) { - RPCHelpMan{ +static RPCHelpMan savemempool() { + return RPCHelpMan{ "savemempool", "Dumps the mempool to disk. It will fail until the previous dump is " "fully loaded.\n", @@ -2451,20 +2508,23 @@ RPCResult{RPCResult::Type::NONE, "", ""}, RPCExamples{HelpExampleCli("savemempool", "") + HelpExampleRpc("savemempool", "")}, - } - .Check(request); + [&](const RPCHelpMan &self, const Config &config, + const JSONRPCRequest &request) -> UniValue { + const CTxMemPool &mempool = EnsureMemPool(request.context); - const CTxMemPool &mempool = EnsureMemPool(request.context); - - if (!mempool.IsLoaded()) { - throw JSONRPCError(RPC_MISC_ERROR, "The mempool was not loaded yet"); - } + 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"); - } + if (!DumpMempool(mempool)) { + throw JSONRPCError(RPC_MISC_ERROR, + "Unable to dump mempool to disk"); + } - return NullUniValue; + return NullUniValue; + }, + }; } namespace { @@ -2533,10 +2593,9 @@ } }; -static UniValue scantxoutset(const Config &config, - const JSONRPCRequest &request) { +static RPCHelpMan scantxoutset() { const auto &ticker = Currency::get().ticker; - RPCHelpMan{ + return RPCHelpMan{ "scantxoutset", "EXPERIMENTAL warning: this call may be removed or changed in future " "releases.\n" @@ -2636,115 +2695,117 @@ "The total amount of all found unspent outputs in " + ticker}, }}, RPCExamples{""}, - } - .Check(request); - - RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR}); - - UniValue result(UniValue::VOBJ); - if (request.params[0].get_str() == "status") { - CoinsViewScanReserver reserver; - if (reserver.reserve()) { - // no scan in progress - return NullUniValue; - } - result.pushKV("progress", g_scan_progress.load()); - return result; - } else if (request.params[0].get_str() == "abort") { - CoinsViewScanReserver reserver; - if (reserver.reserve()) { - // reserve was possible which means no scan was running - return false; - } - // set the abort flag - g_should_abort_scan = true; - return true; - } else if (request.params[0].get_str() == "start") { - CoinsViewScanReserver reserver; - if (!reserver.reserve()) { - throw JSONRPCError( - RPC_INVALID_PARAMETER, - "Scan already in progress, use action \"abort\" or \"status\""); - } - - if (request.params.size() < 2) { - throw JSONRPCError( - RPC_MISC_ERROR, - "scanobjects argument is required for the start action"); - } - - std::set needles; - std::map descriptors; - Amount total_in = Amount::zero(); - - // loop through the scan objects - for (const UniValue &scanobject : - request.params[1].get_array().getValues()) { - FlatSigningProvider provider; - auto scripts = EvalDescriptorStringOrObject(scanobject, provider); - for (const auto &script : scripts) { - std::string inferred = - InferDescriptor(script, provider)->ToString(); - needles.emplace(script); - descriptors.emplace(std::move(script), std::move(inferred)); + [&](const RPCHelpMan &self, const Config &config, + const JSONRPCRequest &request) -> UniValue { + RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR}); + + UniValue result(UniValue::VOBJ); + if (request.params[0].get_str() == "status") { + CoinsViewScanReserver reserver; + if (reserver.reserve()) { + // no scan in progress + return NullUniValue; + } + result.pushKV("progress", g_scan_progress.load()); + return result; + } else if (request.params[0].get_str() == "abort") { + CoinsViewScanReserver reserver; + if (reserver.reserve()) { + // reserve was possible which means no scan was running + return false; + } + // set the abort flag + g_should_abort_scan = true; + return true; + } else if (request.params[0].get_str() == "start") { + CoinsViewScanReserver reserver; + if (!reserver.reserve()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, + "Scan already in progress, use action " + "\"abort\" or \"status\""); + } + + if (request.params.size() < 2) { + throw JSONRPCError(RPC_MISC_ERROR, + "scanobjects argument is required for " + "the start action"); + } + + std::set needles; + std::map descriptors; + Amount total_in = Amount::zero(); + + // loop through the scan objects + for (const UniValue &scanobject : + request.params[1].get_array().getValues()) { + FlatSigningProvider provider; + auto scripts = + EvalDescriptorStringOrObject(scanobject, provider); + for (const auto &script : scripts) { + std::string inferred = + InferDescriptor(script, provider)->ToString(); + needles.emplace(script); + descriptors.emplace(std::move(script), + std::move(inferred)); + } + } + + // Scan the unspent transaction output set for inputs + UniValue unspents(UniValue::VARR); + std::vector input_txos; + std::map coins; + g_should_abort_scan = false; + g_scan_progress = 0; + int64_t count = 0; + std::unique_ptr pcursor; + CBlockIndex *tip; + { + LOCK(cs_main); + ::ChainstateActive().ForceFlushStateToDisk(); + pcursor = std::unique_ptr( + ::ChainstateActive().CoinsDB().Cursor()); + CHECK_NONFATAL(pcursor); + tip = ::ChainActive().Tip(); + CHECK_NONFATAL(tip); + } + NodeContext &node = EnsureNodeContext(request.context); + bool res = FindScriptPubKey( + g_scan_progress, g_should_abort_scan, count, pcursor.get(), + needles, coins, node.rpc_interruption_point); + result.pushKV("success", res); + result.pushKV("txouts", count); + result.pushKV("height", tip->nHeight); + result.pushKV("bestblock", tip->GetBlockHash().GetHex()); + + for (const auto &it : coins) { + const COutPoint &outpoint = it.first; + const Coin &coin = it.second; + const CTxOut &txo = coin.GetTxOut(); + input_txos.push_back(txo); + total_in += txo.nValue; + + UniValue unspent(UniValue::VOBJ); + unspent.pushKV("txid", outpoint.GetTxId().GetHex()); + unspent.pushKV("vout", int32_t(outpoint.GetN())); + unspent.pushKV("scriptPubKey", HexStr(txo.scriptPubKey)); + unspent.pushKV("desc", descriptors[txo.scriptPubKey]); + unspent.pushKV("amount", txo.nValue); + unspent.pushKV("height", int32_t(coin.GetHeight())); + + unspents.push_back(unspent); + } + result.pushKV("unspents", unspents); + result.pushKV("total_amount", total_in); + } else { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid command"); } - } - - // Scan the unspent transaction output set for inputs - UniValue unspents(UniValue::VARR); - std::vector input_txos; - std::map coins; - g_should_abort_scan = false; - g_scan_progress = 0; - int64_t count = 0; - std::unique_ptr pcursor; - CBlockIndex *tip; - { - LOCK(cs_main); - ::ChainstateActive().ForceFlushStateToDisk(); - pcursor = std::unique_ptr( - ::ChainstateActive().CoinsDB().Cursor()); - CHECK_NONFATAL(pcursor); - tip = ::ChainActive().Tip(); - CHECK_NONFATAL(tip); - } - NodeContext &node = EnsureNodeContext(request.context); - bool res = FindScriptPubKey(g_scan_progress, g_should_abort_scan, count, - pcursor.get(), needles, coins, - node.rpc_interruption_point); - result.pushKV("success", res); - result.pushKV("txouts", count); - result.pushKV("height", tip->nHeight); - result.pushKV("bestblock", tip->GetBlockHash().GetHex()); - - for (const auto &it : coins) { - const COutPoint &outpoint = it.first; - const Coin &coin = it.second; - const CTxOut &txo = coin.GetTxOut(); - input_txos.push_back(txo); - total_in += txo.nValue; - - UniValue unspent(UniValue::VOBJ); - unspent.pushKV("txid", outpoint.GetTxId().GetHex()); - unspent.pushKV("vout", int32_t(outpoint.GetN())); - unspent.pushKV("scriptPubKey", HexStr(txo.scriptPubKey)); - unspent.pushKV("desc", descriptors[txo.scriptPubKey]); - unspent.pushKV("amount", txo.nValue); - unspent.pushKV("height", int32_t(coin.GetHeight())); - - unspents.push_back(unspent); - } - result.pushKV("unspents", unspents); - result.pushKV("total_amount", total_in); - } else { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid command"); - } - return result; + return result; + }, + }; } -static UniValue getblockfilter(const Config &config, - const JSONRPCRequest &request) { - RPCHelpMan{ +static RPCHelpMan getblockfilter() { + return RPCHelpMan{ "getblockfilter", "Retrieve a BIP 157 content filter for a particular block.\n", { @@ -2768,67 +2829,73 @@ "dbda81d7e2a3dd146f6ed09\" \"basic\"") + HelpExampleRpc("getblockfilter", "\"00000000c937983704a73af28acdec37b049d214adbda81d7" - "e2a3dd146f6ed09\", \"basic\"")}} - .Check(request); - - const BlockHash block_hash(ParseHashV(request.params[0], "blockhash")); - std::string filtertype_name = "basic"; - if (!request.params[1].isNull()) { - filtertype_name = request.params[1].get_str(); - } - - BlockFilterType filtertype; - if (!BlockFilterTypeByName(filtertype_name, filtertype)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unknown filtertype"); - } + "e2a3dd146f6ed09\", \"basic\"")}, + [&](const RPCHelpMan &self, const Config &config, + const JSONRPCRequest &request) -> UniValue { + const BlockHash block_hash( + ParseHashV(request.params[0], "blockhash")); + std::string filtertype_name = "basic"; + if (!request.params[1].isNull()) { + filtertype_name = request.params[1].get_str(); + } - BlockFilterIndex *index = GetBlockFilterIndex(filtertype); - if (!index) { - throw JSONRPCError(RPC_MISC_ERROR, - "Index is not enabled for filtertype " + - filtertype_name); - } + BlockFilterType filtertype; + if (!BlockFilterTypeByName(filtertype_name, filtertype)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, + "Unknown filtertype"); + } - const CBlockIndex *block_index; - bool block_was_connected; - { - LOCK(cs_main); - block_index = LookupBlockIndex(block_hash); - if (!block_index) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); - } - block_was_connected = block_index->IsValid(BlockValidity::SCRIPTS); - } + BlockFilterIndex *index = GetBlockFilterIndex(filtertype); + if (!index) { + throw JSONRPCError(RPC_MISC_ERROR, + "Index is not enabled for filtertype " + + filtertype_name); + } - bool index_ready = index->BlockUntilSyncedToCurrentChain(); - - BlockFilter filter; - uint256 filter_header; - if (!index->LookupFilter(block_index, filter) || - !index->LookupFilterHeader(block_index, filter_header)) { - int err_code; - std::string errmsg = "Filter not found."; - - if (!block_was_connected) { - err_code = RPC_INVALID_ADDRESS_OR_KEY; - errmsg += " Block was not connected to active chain."; - } else if (!index_ready) { - err_code = RPC_MISC_ERROR; - errmsg += - " Block filters are still in the process of being indexed."; - } else { - err_code = RPC_INTERNAL_ERROR; - errmsg += - " This error is unexpected and indicates index corruption."; - } + const CBlockIndex *block_index; + bool block_was_connected; + { + LOCK(cs_main); + block_index = LookupBlockIndex(block_hash); + if (!block_index) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, + "Block not found"); + } + block_was_connected = + block_index->IsValid(BlockValidity::SCRIPTS); + } - throw JSONRPCError(err_code, errmsg); - } + bool index_ready = index->BlockUntilSyncedToCurrentChain(); + + BlockFilter filter; + uint256 filter_header; + if (!index->LookupFilter(block_index, filter) || + !index->LookupFilterHeader(block_index, filter_header)) { + int err_code; + std::string errmsg = "Filter not found."; + + if (!block_was_connected) { + err_code = RPC_INVALID_ADDRESS_OR_KEY; + errmsg += " Block was not connected to active chain."; + } else if (!index_ready) { + err_code = RPC_MISC_ERROR; + errmsg += " Block filters are still in the process of " + "being indexed."; + } else { + err_code = RPC_INTERNAL_ERROR; + errmsg += " This error is unexpected and indicates index " + "corruption."; + } + + throw JSONRPCError(err_code, errmsg); + } - UniValue ret(UniValue::VOBJ); - ret.pushKV("filter", HexStr(filter.GetEncodedFilter())); - ret.pushKV("header", filter_header.GetHex()); - return ret; + UniValue ret(UniValue::VOBJ); + ret.pushKV("filter", HexStr(filter.GetEncodedFilter())); + ret.pushKV("header", filter_header.GetHex()); + return ret; + }, + }; } /** @@ -2836,9 +2903,8 @@ * * @see SnapshotMetadata */ -static UniValue dumptxoutset(const Config &config, - const JSONRPCRequest &request) { - RPCHelpMan{ +static RPCHelpMan dumptxoutset() { + return RPCHelpMan{ "dumptxoutset", "Write the serialized UTXO set to disk.\n", { @@ -2859,90 +2925,95 @@ {RPCResult::Type::STR, "path", "the absolute path that the snapshot was written to"}, }}, - 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"); - } + RPCExamples{HelpExampleCli("dumptxoutset", "utxo.dat")}, + [&](const RPCHelpMan &self, const Config &config, + const JSONRPCRequest &request) -> UniValue { + 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 pcursor; - CCoinsStats stats; - CBlockIndex *tip; - NodeContext &node = EnsureNodeContext(request.context); - - { - // 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, - CoinStatsHashType::NONE, - node.rpc_interruption_point)) { - throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set"); - } + FILE *file{fsbridge::fopen(temppath, "wb")}; + CAutoFile afile{file, SER_DISK, CLIENT_VERSION}; + std::unique_ptr pcursor; + CCoinsStats stats; + CBlockIndex *tip; + NodeContext &node = EnsureNodeContext(request.context); - pcursor = std::unique_ptr( - ::ChainstateActive().CoinsDB().Cursor()); - tip = LookupBlockIndex(stats.hashBlock); - CHECK_NONFATAL(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, + CoinStatsHashType::NONE, + node.rpc_interruption_point)) { + throw JSONRPCError(RPC_INTERNAL_ERROR, + "Unable to read UTXO set"); + } + + pcursor = std::unique_ptr( + ::ChainstateActive().CoinsDB().Cursor()); + tip = LookupBlockIndex(stats.hashBlock); + CHECK_NONFATAL(tip); + } - SnapshotMetadata metadata{tip->GetBlockHash(), stats.coins_count, - uint64_t(tip->GetChainTxCount())}; + SnapshotMetadata metadata{tip->GetBlockHash(), stats.coins_count, + uint64_t(tip->GetChainTxCount())}; - afile << metadata; + afile << metadata; - COutPoint key; - Coin coin; - unsigned int iter{0}; + COutPoint key; + Coin coin; + unsigned int iter{0}; - while (pcursor->Valid()) { - if (iter % 5000 == 0) { - node.rpc_interruption_point(); - } - ++iter; - if (pcursor->GetKey(key) && pcursor->GetValue(coin)) { - afile << key; - afile << coin; - } + while (pcursor->Valid()) { + if (iter % 5000 == 0) { + node.rpc_interruption_point(); + } + ++iter; + if (pcursor->GetKey(key) && pcursor->GetValue(coin)) { + afile << key; + afile << coin; + } - pcursor->Next(); - } + pcursor->Next(); + } - afile.fclose(); - fs::rename(temppath, path); + 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; + 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; + }, + }; } void RegisterBlockchainRPCCommands(CRPCTable &t) {