Merge pull request #4708 from kittywhiskers/json_cleanup

merge bitcoin 15746, 16240, 17192, 17318, 17809, 18098, 18444, 18607, 19100, 19386: rpc backports
This commit is contained in:
UdjinM6 2022-03-15 12:08:35 +03:00 committed by GitHub
commit f1a64744cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 2215 additions and 2028 deletions

View File

@ -257,9 +257,10 @@ BITCOIN_CORE_H = \
rpc/client.h \
rpc/mining.h \
rpc/protocol.h \
rpc/server.h \
rpc/rawtransaction_util.h \
rpc/register.h \
rpc/request.h \
rpc/server.h \
rpc/util.h \
saltedhasher.h \
scheduler.h \
@ -292,6 +293,7 @@ BITCOIN_CORE_H = \
unordered_lru_cache.h \
util/bip32.h \
util/bytevectorhash.h \
util/check.h \
util/error.h \
util/fees.h \
util/spanparsing.h \
@ -678,7 +680,7 @@ libdash_util_a_SOURCES = \
interfaces/handler.cpp \
logging.cpp \
random.cpp \
rpc/protocol.cpp \
rpc/request.cpp \
stacktraces.cpp \
support/cleanse.cpp \
sync.cpp \

View File

@ -12,6 +12,7 @@
#include <clientversion.h>
#include <rpc/client.h>
#include <rpc/protocol.h>
#include <rpc/request.h>
#include <stacktraces.h>
#include <util/system.h>
#include <util/strencodings.h>

View File

@ -11,6 +11,7 @@
#include <primitives/block.h>
#include <primitives/transaction.h>
#include <rpc/blockchain.h>
#include <rpc/protocol.h>
#include <rpc/server.h>
#include <streams.h>
#include <sync.h>

View File

@ -11,12 +11,12 @@
#include <chain.h>
#include <chainparams.h>
#include <coins.h>
#include <node/coinstats.h>
#include <core_io.h>
#include <consensus/validation.h>
#include <index/blockfilterindex.h>
#include <key_io.h>
#include <index/txindex.h>
#include <key_io.h>
#include <node/coinstats.h>
#include <policy/feerate.h>
#include <policy/policy.h>
#include <primitives/transaction.h>
@ -40,7 +40,6 @@
#include <llmq/chainlocks.h>
#include <llmq/instantsend.h>
#include <assert.h>
#include <stdint.h>
#include <univalue.h>
@ -67,7 +66,7 @@ extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue&
*/
double GetDifficulty(const CBlockIndex* blockindex)
{
assert(blockindex);
CHECK_NONFATAL(blockindex);
int nShift = (blockindex->nBits >> 24) & 0xff;
double dDiff =
@ -190,8 +189,7 @@ static UniValue getblockcount(const JSONRPCRequest& request)
"The genesis block has height 0.\n",
{},
RPCResult{
"n (numeric) The current block count\n"
},
RPCResult::Type::NUM, "", "The current block count"},
RPCExamples{
HelpExampleCli("getblockcount", "")
+ HelpExampleRpc("getblockcount", "")
@ -210,8 +208,7 @@ static UniValue getbestblockhash(const JSONRPCRequest& request)
"\nReturns the hash of the best (tip) block in the most-work fully-validated chain.\n",
{},
RPCResult{
"\"hex\" (string) the block hash, hex-encoded\n"
},
RPCResult::Type::STR_HEX, "", "the block hash, hex-encoded"},
RPCExamples{
HelpExampleCli("getbestblockhash", "")
+ HelpExampleRpc("getbestblockhash", "")
@ -278,11 +275,11 @@ static UniValue waitfornewblock(const JSONRPCRequest& request)
{"timeout", RPCArg::Type::NUM, /* default */ "0", "Time in milliseconds to wait for a response. 0 indicates no timeout."},
},
RPCResult{
"{ (json object)\n"
" \"hash\" : { (string) The blockhash\n"
" \"height\" : { (int) Block height\n"
"}\n"
},
RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::STR_HEX, "hash", "The blockhash"},
{RPCResult::Type::NUM, "height", "Block height"},
}},
RPCExamples{
HelpExampleCli("waitfornewblock", "1000")
+ HelpExampleRpc("waitfornewblock", "1000")
@ -320,11 +317,11 @@ static UniValue waitforblock(const JSONRPCRequest& request)
{"timeout", RPCArg::Type::NUM, /* default */ "0", "Time in milliseconds to wait for a response. 0 indicates no timeout."},
},
RPCResult{
"{ (json object)\n"
" \"hash\" : { (string) The blockhash\n"
" \"height\" : { (int) Block height\n"
"}\n"
},
RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::STR_HEX, "hash", "The blockhash"},
{RPCResult::Type::NUM, "height", "Block height"},
}},
RPCExamples{
HelpExampleCli("waitforblock", "\"0000000000079f8ef3d2c688c244eb7a4570b24c9ed7b4a8c619eb02596f8862\" 1000")
+ HelpExampleRpc("waitforblock", "\"0000000000079f8ef3d2c688c244eb7a4570b24c9ed7b4a8c619eb02596f8862\", 1000")
@ -366,11 +363,11 @@ static UniValue waitforblockheight(const JSONRPCRequest& request)
{"timeout", RPCArg::Type::NUM, /* default */ "0", "Time in milliseconds to wait for a response. 0 indicates no timeout."},
},
RPCResult{
"{ (json object)\n"
" \"hash\" : { (string) The blockhash\n"
" \"height\" : { (int) Block height\n"
"}\n"
},
RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::STR_HEX, "hash", "The blockhash"},
{RPCResult::Type::NUM, "height", "Block height"},
}},
RPCExamples{
HelpExampleCli("waitforblockheight", "100 1000")
+ HelpExampleRpc("waitforblockheight", "100, 1000")
@ -425,8 +422,7 @@ static UniValue getdifficulty(const JSONRPCRequest& request)
"\nReturns the proof-of-work difficulty as a multiple of the minimum difficulty.\n",
{},
RPCResult{
"n.nnn (numeric) the proof-of-work difficulty as a multiple of the minimum difficulty.\n"
},
RPCResult::Type::NUM, "", "the proof-of-work difficulty as a multiple of the minimum difficulty."},
RPCExamples{
HelpExampleCli("getdifficulty", "")
+ HelpExampleRpc("getdifficulty", "")
@ -458,10 +454,10 @@ static std::string EntryDescriptionString()
" \"ancestor\" : n, (numeric) modified fees (see above) of in-mempool ancestors (including this one) in " + CURRENCY_UNIT + "\n"
" \"descendant\" : n, (numeric) modified fees (see above) of in-mempool descendants (including this one) in " + CURRENCY_UNIT + "\n"
" }\n"
" \"depends\" : [ (array) unconfirmed transactions used as inputs for this transaction\n"
" \"depends\" : [ (json array) unconfirmed transactions used as inputs for this transaction\n"
" \"transactionid\", (string) parent transaction id\n"
" ... ],\n"
" \"spentby\" : [ (array) unconfirmed transactions spending outputs from this transaction\n"
" \"spentby\" : [ (json array) unconfirmed transactions spending outputs from this transaction\n"
" \"transactionid\", (string) child transaction id\n"
" ... ]\n"
" \"instantlock\" : true|false (boolean) True if this transaction was locked via InstantSend\n";
@ -802,8 +798,7 @@ static UniValue getblockhash(const JSONRPCRequest& request)
{"height", RPCArg::Type::NUM, RPCArg::Optional::NO, "The height index"},
},
RPCResult{
"\"hash\" (string) The block hash\n"
},
RPCResult::Type::STR_HEX, "", "The block hash"},
RPCExamples{
HelpExampleCli("getblockhash", "1000")
+ HelpExampleRpc("getblockhash", "1000")
@ -1093,12 +1088,11 @@ static UniValue getblock(const JSONRPCRequest& request)
"If verbosity is 2, returns an Object with information about block <hash> and information about each transaction. \n",
{
{"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash"},
{"verbosity", RPCArg::Type::NUM, /* default */ "1", "0 for hex-encoded data, 1 for a json object, and 2 for json object with transaction data"},
{"verbosity|verbose", RPCArg::Type::NUM, /* default */ "1", "0 for hex-encoded data, 1 for a json object, and 2 for json object with transaction data"},
},
{
RPCResult{"for verbosity = 0",
"\"data\" (string) A string that is serialized, hex-encoded data for block 'hash'.\n"
},
RPCResult::Type::STR_HEX, "", "A string that is serialized, hex-encoded data for block 'hash'"},
RPCResult{"for verbosity = 1",
"{\n"
" \"hash\" : \"hash\", (string) the block hash (same as provided)\n"
@ -1186,8 +1180,7 @@ static UniValue pruneblockchain(const JSONRPCRequest& request)
" to prune blocks whose block time is at least 2 hours older than the provided timestamp."},
},
RPCResult{
"n (numeric) Height of the last block pruned.\n"
},
RPCResult::Type::NUM, "", "Height of the last block pruned"},
RPCExamples{
HelpExampleCli("pruneblockchain", "1000")
+ HelpExampleRpc("pruneblockchain", "1000")
@ -1227,7 +1220,7 @@ static UniValue pruneblockchain(const JSONRPCRequest& request)
PruneBlockFilesManual(height);
const CBlockIndex* block = ::ChainActive().Tip();
assert(block);
CHECK_NONFATAL(block);
while (block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) {
block = block->pprev;
}
@ -1243,17 +1236,17 @@ static UniValue gettxoutsetinfo(const JSONRPCRequest& request)
"Note this call may take some time.\n",
{},
RPCResult{
"{\n"
" \"height\":n, (numeric) The current block height (index)\n"
" \"bestblock\": \"hex\", (string) The hash of the block at the tip of the chain\n"
" \"transactions\": n, (numeric) The number of transactions with unspent outputs\n"
" \"txouts\": n, (numeric) The number of unspent transaction outputs\n"
" \"bogosize\": n, (numeric) A meaningless metric for UTXO set size\n"
" \"hash_serialized_2\": \"hash\", (string) The serialized hash\n"
" \"disk_size\": n, (numeric) The estimated size of the chainstate on disk\n"
" \"total_amount\": x.xxx (numeric) The total amount\n"
"}\n"
},
RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::NUM, "height", "The current block height (index)"},
{RPCResult::Type::STR_HEX, "bestblock", "The hash of the block at the tip of the chain"},
{RPCResult::Type::NUM, "transactions", "The number of transactions with unspent outputs"},
{RPCResult::Type::NUM, "txouts", "The number of unspent transaction outputs"},
{RPCResult::Type::NUM, "bogosize", "A meaningless metric for UTXO set size"},
{RPCResult::Type::STR_HEX, "hash_serialized_2", "The serialized hash"},
{RPCResult::Type::NUM, "disk_size", "The estimated size of the chainstate on disk"},
{RPCResult::Type::STR_AMOUNT, "total_amount", "The total amount"},
}},
RPCExamples{
HelpExampleCli("gettxoutsetinfo", "")
+ HelpExampleRpc("gettxoutsetinfo", "")
@ -1376,8 +1369,7 @@ static UniValue verifychain(const JSONRPCRequest& request)
{"nblocks", RPCArg::Type::NUM, /* default */ strprintf("%d, 0=all", nCheckDepth), "The number of blocks to check."},
},
RPCResult{
"true|false (boolean) Verified or not\n"
},
RPCResult::Type::BOOL, "", "Verified or not"},
RPCExamples{
HelpExampleCli("verifychain", "")
+ HelpExampleRpc("verifychain", "")
@ -1483,30 +1475,30 @@ UniValue getblockchaininfo(const JSONRPCRequest& request)
" \"difficulty\" : xxxxxx, (numeric) the current difficulty\n"
" \"mediantime\" : xxxxxx, (numeric) median time for the current best block\n"
" \"verificationprogress\" : xxxx, (numeric) estimate of verification progress [0..1]\n"
" \"initialblockdownload\": xxxx, (bool) (debug information) estimate of whether this node is in Initial Block Download mode.\n"
" \"initialblockdownload\" : xxxx, (boolean) (debug information) estimate of whether this node is in Initial Block Download mode.\n"
" \"chainwork\" : \"xxxx\" (string) total amount of work in active chain, in hexadecimal\n"
" \"size_on_disk\" : xxxxxx, (numeric) the estimated size of the block and undo files on disk\n"
" \"pruned\" : xx, (boolean) if the blocks are subject to pruning\n"
" \"pruneheight\" : xxxxxx, (numeric) lowest-height complete block stored (only present if pruning is enabled)\n"
" \"automatic_pruning\" : xx, (boolean) whether automatic pruning is enabled (only present if pruning is enabled)\n"
" \"prune_target_size\" : xxxxxx, (numeric) the target size used by pruning (only present if automatic pruning is enabled)\n"
" \"softforks\": [ (array) status of softforks in progress\n"
" \"softforks\" : [ (json array) status of softforks in progress\n"
" {\n"
" \"id\" : \"xxxx\", (string) name of softfork\n"
" \"version\" : xx, (numeric) block version\n"
" \"reject\": { (object) progress toward rejecting pre-softfork blocks\n"
" \"reject\" : { (json object) progress toward rejecting pre-softfork blocks\n"
" \"status\" : xx, (boolean) true if threshold reached\n"
" },\n"
" }, ...\n"
" ],\n"
" \"bip9_softforks\": { (object) status of BIP9 softforks in progress\n"
" \"bip9_softforks\": { (json object) status of BIP9 softforks in progress\n"
" \"xxxx\" : { (string) name of the softfork\n"
" \"status\" : \"xxxx\", (string) one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\"\n"
" \"bit\" : xx, (numeric) the bit (0-28) in the block version field used to signal this softfork (only for \"started\" status)\n"
" \"start_time\" : xx, (numeric) the minimum median time past of a block at which the bit gains its meaning\n"
" \"timeout\" : xx, (numeric) the median time past of a block at which the deployment is considered failed if not yet locked in\n"
" \"since\" : xx, (numeric) height of the first block to which the status applies\n"
" \"statistics\": { (object) numeric statistics about BIP9 signalling for a softfork (only for \"started\" status)\n"
" \"statistics\" : { (json object) numeric statistics about BIP9 signalling for a softfork (only for \"started\" status)\n"
" \"period\" : xx, (numeric) the length in blocks of the BIP9 signalling period \n"
" \"threshold\" : xx, (numeric) the number of blocks with the version bit set required to activate the feature \n"
" \"elapsed\" : xx, (numeric) the number of blocks elapsed since the beginning of the current period \n"
@ -1543,7 +1535,7 @@ UniValue getblockchaininfo(const JSONRPCRequest& request)
obj.pushKV("pruned", fPruneMode);
if (fPruneMode) {
const CBlockIndex* block = tip;
assert(block);
CHECK_NONFATAL(block);
while (block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) {
block = block->pprev;
}
@ -1937,7 +1929,7 @@ static UniValue getchaintxstats(const JSONRPCRequest& request)
}
}
assert(pindex != nullptr);
CHECK_NONFATAL(pindex != nullptr);
if (request.params[0].isNull()) {
blockcount = std::max(0, std::min(blockcount, pindex->nHeight - 1));
@ -2119,7 +2111,7 @@ static UniValue getblockstats(const JSONRPCRequest& request)
}
}
assert(pindex != nullptr);
CHECK_NONFATAL(pindex != nullptr);
std::set<std::string> stats;
if (!request.params[1].isNull()) {
@ -2208,7 +2200,7 @@ static UniValue getblockstats(const JSONRPCRequest& request)
}
CAmount txfee = tx_total_in - tx_total_out;
assert(MoneyRange(txfee));
CHECK_NONFATAL(MoneyRange(txfee));
if (do_medianfee) {
fee_array.push_back(txfee);
}
@ -2448,7 +2440,7 @@ public:
explicit CoinsViewScanReserver() : m_could_reserve(false) {}
bool reserve() {
assert (!m_could_reserve);
CHECK_NONFATAL(!m_could_reserve);
std::lock_guard<std::mutex> lock(g_utxosetscan);
if (g_scan_in_progress) {
return false;
@ -2503,24 +2495,26 @@ UniValue scantxoutset(const JSONRPCRequest& request)
"[scanobjects,...]"},
},
RPCResult{
"{\n"
" \"success\": true|false, (boolean) Whether the scan was completed\n"
" \"txouts\": n, (numeric) The number of unspent transaction outputs scanned\n"
" \"height\": n, (numeric) The current block height (index)\n"
" \"bestblock\": \"hex\", (string) The hash of the block at the tip of the chain\n"
" \"unspents\": [\n"
" {\n"
" \"txid\": \"hash\", (string) The transaction id\n"
" \"vout\": n, (numeric) The vout value\n"
" \"scriptPubKey\": \"script\", (string) The script key\n"
" \"desc\": \"descriptor\", (string) A specialized descriptor for the matched scriptPubKey\n"
" \"amount\": x.xxx, (numeric) The total amount in " + CURRENCY_UNIT + " of the unspent output\n"
" \"height\": n, (numeric) Height of the unspent transaction output\n"
" }\n"
" ,...],\n"
" \"total_amount\": x.xxx, (numeric) The total amount of all found unspent outputs in " + CURRENCY_UNIT + "\n"
"]\n"
},
RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::BOOL, "success", "Whether the scan was completed"},
{RPCResult::Type::NUM, "txouts", "The number of unspent transaction outputs scanned"},
{RPCResult::Type::NUM, "height", "The current block height (index)"},
{RPCResult::Type::STR_HEX, "bestblock", "The hash of the block at the tip of the chain"},
{RPCResult::Type::ARR, "unspents", "",
{
{RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::STR_HEX, "txid", "The transaction id"},
{RPCResult::Type::NUM, "vout", "The vout value"},
{RPCResult::Type::STR_HEX, "scriptPubKey", "The script key"},
{RPCResult::Type::STR, "desc", "A specialized descriptor for the matched scriptPubKey"},
{RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " of the unspent output"},
{RPCResult::Type::NUM, "height", "Height of the unspent transaction output"},
}},
}},
{RPCResult::Type::STR_AMOUNT, "total_amount", "The total amount of all found unspent outputs in " + CURRENCY_UNIT},
}},
RPCExamples{""},
}.ToString()
);
@ -2613,9 +2607,9 @@ UniValue scantxoutset(const JSONRPCRequest& request)
LOCK(cs_main);
::ChainstateActive().ForceFlushStateToDisk();
pcursor = std::unique_ptr<CCoinsViewCursor>(::ChainstateActive().CoinsDB().Cursor());
assert(pcursor);
CHECK_NONFATAL(pcursor);
tip = ::ChainActive().Tip();
assert(tip);
CHECK_NONFATAL(tip);
}
bool res = FindScriptPubKey(g_scan_progress, g_should_abort_scan, count, pcursor.get(), needles, coins);
result.pushKV("success", res);

View File

@ -18,11 +18,6 @@
#ifdef ENABLE_WALLET
static UniValue coinjoin(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
return NullUniValue;
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
RPCHelpMan{"coinjoin",
@ -37,6 +32,10 @@ static UniValue coinjoin(const JSONRPCRequest& request)
RPCExamples{""},
}.ToString());
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
if (fMasternodeMode)
throw JSONRPCError(RPC_INTERNAL_ERROR, "Client-side mixing is not supported on masternodes");
@ -103,15 +102,15 @@ static UniValue getcoinjoininfo(const JSONRPCRequest& request)
RPCResults{
{"for regular nodes",
"{\n"
" \"enabled\": true|false, (bool) Whether mixing functionality is enabled\n"
" \"multisession\": true|false, (bool) Whether CoinJoin Multisession option is enabled\n"
" \"enabled\" : true|false, (boolean) Whether mixing functionality is enabled\n"
" \"multisession\" : true|false, (boolean) Whether CoinJoin Multisession option is enabled\n"
" \"max_sessions\" : xxx, (numeric) How many parallel mixing sessions can there be at once\n"
" \"max_rounds\" : xxx, (numeric) How many rounds to mix\n"
" \"max_amount\" : xxx, (numeric) Target CoinJoin balance in " + CURRENCY_UNIT + "\n"
" \"denoms_goal\" : xxx, (numeric) How many inputs of each denominated amount to target\n"
" \"denoms_hardcap\" : xxx, (numeric) Maximum limit of how many inputs of each denominated amount to create\n"
" \"queue_size\" : xxx, (numeric) How many queues there are currently on the network\n"
" \"running\": true|false, (bool) Whether mixing is currently running\n"
" \"running\" : true|false, (boolean) Whether mixing is currently running\n"
" \"sessions\" : (array of json objects)\n"
" [\n"
" {\n"

View File

@ -24,9 +24,8 @@
#include <wallet/wallet.h>
#endif // ENABLE_WALLET
static void gobject_count_help()
static void gobject_count_help(const JSONRPCRequest& request)
{
throw std::runtime_error(
RPCHelpMan{"gobject count",
"Count governance objects and votes\n",
{
@ -34,13 +33,13 @@ static void gobject_count_help()
},
RPCResults{},
RPCExamples{""}
}.ToString());
}.Check(request);
}
static UniValue gobject_count(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 2)
gobject_count_help();
gobject_count_help(request);
std::string strMode{"json"};
@ -49,14 +48,13 @@ static UniValue gobject_count(const JSONRPCRequest& request)
}
if (strMode != "json" && strMode != "all")
gobject_count_help();
gobject_count_help(request);
return strMode == "json" ? governance.ToJson() : governance.ToString();
}
static void gobject_deserialize_help()
static void gobject_deserialize_help(const JSONRPCRequest& request)
{
throw std::runtime_error(
RPCHelpMan {"gobject deserialize",
"Deserialize governance object from hex string to JSON\n",
{
@ -64,13 +62,13 @@ static void gobject_deserialize_help()
},
RPCResults{},
RPCExamples{""}
}.ToString());
}.Check(request);
}
static UniValue gobject_deserialize(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 2)
gobject_deserialize_help();
gobject_deserialize_help(request);
std::string strHex = request.params[1].get_str();
@ -83,9 +81,8 @@ static UniValue gobject_deserialize(const JSONRPCRequest& request)
return u.write().c_str();
}
static void gobject_check_help()
static void gobject_check_help(const JSONRPCRequest& request)
{
throw std::runtime_error(
RPCHelpMan{"gobject check",
"Validate governance object data (proposal only)\n",
{
@ -93,13 +90,13 @@ static void gobject_check_help()
},
RPCResults{},
RPCExamples{""}
}.ToString());
}.Check(request);
}
static UniValue gobject_check(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 2)
gobject_check_help();
gobject_check_help(request);
// ASSEMBLE NEW GOVERNANCE OBJECT FROM USER PARAMETERS
@ -132,12 +129,11 @@ static UniValue gobject_check(const JSONRPCRequest& request)
}
#ifdef ENABLE_WALLET
static void gobject_prepare_help(CWallet* const pwallet)
static void gobject_prepare_help(const JSONRPCRequest& request)
{
throw std::runtime_error(
RPCHelpMan{"gobject prepare",
"Prepare governance object by signing and creating tx\n"
+ HelpRequiringPassphrase(pwallet) + "\n",
+ HelpRequiringPassphrase() + "\n",
{
{"parent-hash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "hash of the parent object, \"0\" is root"},
{"revision", RPCArg::Type::NUM, RPCArg::Optional::NO, "object revision in the system"},
@ -149,18 +145,17 @@ static void gobject_prepare_help(CWallet* const pwallet)
},
RPCResults{},
RPCExamples{""}
}.ToString());
}.Check(request);
}
static UniValue gobject_prepare(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
return NullUniValue;
if (request.fHelp || (request.params.size() != 5 && request.params.size() != 6 && request.params.size() != 8))
gobject_prepare_help(pwallet);
gobject_prepare_help(request);
EnsureWalletIsUnlocked(pwallet);
@ -260,31 +255,29 @@ static UniValue gobject_prepare(const JSONRPCRequest& request)
return tx->GetHash().ToString();
}
static void gobject_list_prepared_help(CWallet* const pwallet)
static void gobject_list_prepared_help(const JSONRPCRequest& request)
{
throw std::runtime_error(
RPCHelpMan{"gobject list-prepared",
"Returns a list of governance objects prepared by this wallet with \"gobject prepare\" sorted by their creation time.\n"
+ HelpRequiringPassphrase(pwallet) + "\n",
+ HelpRequiringPassphrase() + "\n",
{
{"count", RPCArg::Type::NUM, /* default */ "10", "Maximum number of objects to return."},
},
RPCResults{},
RPCExamples{""}
}.ToString());
}.Check(request);
}
static UniValue gobject_list_prepared(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
return NullUniValue;
if (request.fHelp || (request.params.size() > 2)) {
gobject_list_prepared_help(pwallet);
gobject_list_prepared_help(request);
}
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
EnsureWalletIsUnlocked(pwallet);
int64_t nCount = request.params.size() > 1 ? ParseInt64V(request.params[1], "count") : 10;
@ -312,9 +305,8 @@ static UniValue gobject_list_prepared(const JSONRPCRequest& request)
}
#endif // ENABLE_WALLET
static void gobject_submit_help()
static void gobject_submit_help(const JSONRPCRequest& request)
{
throw std::runtime_error(
RPCHelpMan{"gobject submit",
"Submit governance object to network\n",
{
@ -326,13 +318,13 @@ static void gobject_submit_help()
},
RPCResults{},
RPCExamples{""}
}.ToString());
}.Check(request);
}
static UniValue gobject_submit(const JSONRPCRequest& request)
{
if (request.fHelp || ((request.params.size() < 5) || (request.params.size() > 6)))
gobject_submit_help();
gobject_submit_help(request);
if(!masternodeSync.IsBlockchainSynced()) {
throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Must wait for client to sync with masternode network. Try again in a minute or so.");
@ -430,9 +422,8 @@ static UniValue gobject_submit(const JSONRPCRequest& request)
return govobj.GetHash().ToString();
}
static void gobject_vote_conf_help()
static void gobject_vote_conf_help(const JSONRPCRequest& request)
{
throw std::runtime_error(
RPCHelpMan{"gobject vote-conf",
"Vote on a governance object by masternode configured in dash.conf\n",
{
@ -442,13 +433,13 @@ static void gobject_vote_conf_help()
},
RPCResults{},
RPCExamples{""}
}.ToString());
}.Check(request);
}
static UniValue gobject_vote_conf(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 4)
gobject_vote_conf_help();
gobject_vote_conf_help(request);
uint256 hash;
@ -604,12 +595,11 @@ static UniValue VoteWithMasternodes(const std::map<uint256, CKey>& keys,
}
#ifdef ENABLE_WALLET
static void gobject_vote_many_help(CWallet* const pwallet)
static void gobject_vote_many_help(const JSONRPCRequest& request)
{
throw std::runtime_error(
RPCHelpMan{"gobject vote-many",
"Vote on a governance object by all masternodes for which the voting key is present in the local wallet\n"
+ HelpRequiringPassphrase(pwallet) + "\n",
+ HelpRequiringPassphrase() + "\n",
{
{"governance-hash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "hash of the governance object"},
{"vote", RPCArg::Type::STR, RPCArg::Optional::NO, "vote, possible values: [funding|valid|delete|endorsed]"},
@ -617,18 +607,17 @@ static void gobject_vote_many_help(CWallet* const pwallet)
},
RPCResults{},
RPCExamples{""}
}.ToString());
}.Check(request);
}
static UniValue gobject_vote_many(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
return NullUniValue;
if (request.fHelp || request.params.size() != 4)
gobject_vote_many_help(pwallet);
gobject_vote_many_help(request);
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
uint256 hash = ParseHashV(request.params[1], "Object hash");
std::string strVoteSignal = request.params[2].get_str();
@ -661,12 +650,11 @@ static UniValue gobject_vote_many(const JSONRPCRequest& request)
return VoteWithMasternodes(votingKeys, hash, eVoteSignal, eVoteOutcome);
}
static void gobject_vote_alias_help(CWallet* const pwallet)
static void gobject_vote_alias_help(const JSONRPCRequest& request)
{
throw std::runtime_error(
RPCHelpMan{"gobject vote-alias",
"Vote on a governance object by masternode's voting key (if present in local wallet)\n"
+ HelpRequiringPassphrase(pwallet) + "\n",
+ HelpRequiringPassphrase() + "\n",
{
{"governance-hash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "hash of the governance object"},
{"vote", RPCArg::Type::STR, RPCArg::Optional::NO, "vote, possible values: [funding|valid|delete|endorsed]"},
@ -675,18 +663,17 @@ static void gobject_vote_alias_help(CWallet* const pwallet)
},
RPCResults{},
RPCExamples{""}
}.ToString());
}.Check(request);
}
static UniValue gobject_vote_alias(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
return NullUniValue;
if (request.fHelp || request.params.size() != 5)
gobject_vote_alias_help(pwallet);
gobject_vote_alias_help(request);
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
uint256 hash = ParseHashV(request.params[1], "Object hash");
std::string strVoteSignal = request.params[2].get_str();
@ -782,9 +769,8 @@ static UniValue ListObjects(const std::string& strCachedSignal, const std::strin
return objResult;
}
static void gobject_list_help()
static void gobject_list_help(const JSONRPCRequest& request)
{
throw std::runtime_error(
RPCHelpMan{"gobject list",
"List governance objects (can be filtered by signal and/or object type)\n",
{
@ -793,13 +779,13 @@ static void gobject_list_help()
},
RPCResults{},
RPCExamples{""}
}.ToString());
}.Check(request);
}
static UniValue gobject_list(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 3)
gobject_list_help();
gobject_list_help(request);
std::string strCachedSignal = "valid";
if (!request.params[1].isNull()) {
@ -818,9 +804,8 @@ static UniValue gobject_list(const JSONRPCRequest& request)
return ListObjects(strCachedSignal, strType, 0);
}
static void gobject_diff_help()
static void gobject_diff_help(const JSONRPCRequest& request)
{
throw std::runtime_error(
RPCHelpMan{"gobject diff",
"List differences since last diff or list\n",
{
@ -829,13 +814,13 @@ static void gobject_diff_help()
},
RPCResults{},
RPCExamples{""}
}.ToString());
}.Check(request);
}
static UniValue gobject_diff(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 3)
gobject_diff_help();
gobject_diff_help(request);
std::string strCachedSignal = "valid";
if (!request.params[1].isNull()) {
@ -854,9 +839,8 @@ static UniValue gobject_diff(const JSONRPCRequest& request)
return ListObjects(strCachedSignal, strType, governance.GetLastDiffTime());
}
static void gobject_get_help()
static void gobject_get_help(const JSONRPCRequest& request)
{
throw std::runtime_error(
RPCHelpMan{"gobject get",
"Get governance object by hash\n",
{
@ -864,13 +848,13 @@ static void gobject_get_help()
},
RPCResults{},
RPCExamples{""}
}.ToString());
}.Check(request);
}
static UniValue gobject_get(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 2)
gobject_get_help();
gobject_get_help(request);
// COLLECT VARIABLES FROM OUR USER
uint256 hash = ParseHashV(request.params[1], "GovObj hash");
@ -946,9 +930,8 @@ static UniValue gobject_get(const JSONRPCRequest& request)
return objResult;
}
static void gobject_getcurrentvotes_help()
static void gobject_getcurrentvotes_help(const JSONRPCRequest& request)
{
throw std::runtime_error(
RPCHelpMan{"gobject getcurrentvotes",
"Get only current (tallying) votes for a governance object hash (does not include old votes)\n",
{
@ -958,13 +941,13 @@ static void gobject_getcurrentvotes_help()
},
RPCResults{},
RPCExamples{""}
}.ToString());
}.Check(request);
}
static UniValue gobject_getcurrentvotes(const JSONRPCRequest& request)
{
if (request.fHelp || (request.params.size() != 2 && request.params.size() != 4))
gobject_getcurrentvotes_help();
gobject_getcurrentvotes_help(request);
// COLLECT PARAMETERS FROM USER
@ -1003,7 +986,6 @@ static UniValue gobject_getcurrentvotes(const JSONRPCRequest& request)
[[ noreturn ]] static void gobject_help()
{
throw std::runtime_error(
RPCHelpMan {"gobject",
"Set of commands to manage governance objects.\n"
"\nAvailable commands:\n"
@ -1030,7 +1012,7 @@ static UniValue gobject_getcurrentvotes(const JSONRPCRequest& request)
{},
RPCResults{},
RPCExamples{""}
}.ToString());
}.Throw();
}
static UniValue gobject(const JSONRPCRequest& request)

View File

@ -25,9 +25,8 @@
static UniValue masternodelist(const JSONRPCRequest& request);
static void masternode_list_help()
static void masternode_list_help(const JSONRPCRequest& request)
{
throw std::runtime_error(
RPCHelpMan{"masternodelist",
"Get a list of masternodes in different modes. This call is identical to 'masternode list' call.\n"
"Available modes:\n"
@ -52,13 +51,13 @@ static void masternode_list_help()
},
RPCResults{},
RPCExamples{""},
}.ToString());
}.Check(request);
}
static UniValue masternode_list(const JSONRPCRequest& request)
{
if (request.fHelp)
masternode_list_help();
masternode_list_help(request);
JSONRPCRequest newRequest = request;
newRequest.params.setArray();
// forward params but skip "list"
@ -68,9 +67,8 @@ static UniValue masternode_list(const JSONRPCRequest& request)
return masternodelist(newRequest);
}
static void masternode_connect_help()
static void masternode_connect_help(const JSONRPCRequest& request)
{
throw std::runtime_error(
RPCHelpMan{"masternode connect",
"Connect to given masternode\n",
{
@ -78,13 +76,13 @@ static void masternode_connect_help()
},
RPCResults{},
RPCExamples{""}
}.ToString());
}.Check(request);
}
static UniValue masternode_connect(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 2)
masternode_connect_help();
masternode_connect_help(request);
std::string strAddress = request.params[1].get_str();
@ -100,21 +98,20 @@ static UniValue masternode_connect(const JSONRPCRequest& request)
return "successfully connected";
}
static void masternode_count_help()
static void masternode_count_help(const JSONRPCRequest& request)
{
throw std::runtime_error(
RPCHelpMan{"masternode count",
"Get information about number of masternodes.\n",
{},
RPCResults{},
RPCExamples{""}
}.ToString());
}.Check(request);
}
static UniValue masternode_count(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 1)
masternode_count_help();
masternode_count_help(request);
auto mnList = deterministicMNManager->GetListAtChainTip();
int total = mnList.GetAllMNsCount();
@ -148,74 +145,69 @@ static UniValue GetNextMasternodeForPayment(int heightShift)
return obj;
}
static void masternode_winner_help()
static void masternode_winner_help(const JSONRPCRequest& request)
{
if (!IsDeprecatedRPCEnabled("masternode_winner")) {
throw std::runtime_error("DEPRECATED: set -deprecatedrpc=masternode_winner to enable it");
}
throw std::runtime_error(
RPCHelpMan{"masternode winner",
"Print info on next masternode winner to vote for\n",
{},
RPCResults{},
RPCExamples{""}
}.ToString());
}.Check(request);
}
static UniValue masternode_winner(const JSONRPCRequest& request)
{
if (request.fHelp || !IsDeprecatedRPCEnabled("masternode_winner"))
masternode_winner_help();
masternode_winner_help(request);
return GetNextMasternodeForPayment(10);
}
static void masternode_current_help()
static void masternode_current_help(const JSONRPCRequest& request)
{
if (!IsDeprecatedRPCEnabled("masternode_current")) {
throw std::runtime_error("DEPRECATED: set -deprecatedrpc=masternode_current to enable it");
}
throw std::runtime_error(
RPCHelpMan{"masternode current",
"Print info on current masternode winner to be paid the next block (calculated locally)\n",
{},
RPCResults{},
RPCExamples{""}
}.ToString());
}.Check(request);
}
static UniValue masternode_current(const JSONRPCRequest& request)
{
if (request.fHelp || !IsDeprecatedRPCEnabled("masternode_current"))
masternode_current_help();
masternode_current_help(request);
return GetNextMasternodeForPayment(1);
}
#ifdef ENABLE_WALLET
static void masternode_outputs_help()
static void masternode_outputs_help(const JSONRPCRequest& request)
{
throw std::runtime_error(
RPCHelpMan{"masternode outputs",
"Print masternode compatible outputs\n",
{},
RPCResults{},
RPCExamples{""}
}.ToString());
}.Check(request);
}
static UniValue masternode_outputs(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
return NullUniValue;
if (request.fHelp)
masternode_outputs_help();
masternode_outputs_help(request);
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
// Find possible candidates
std::vector<COutput> vPossibleCoins;
@ -236,21 +228,20 @@ static UniValue masternode_outputs(const JSONRPCRequest& request)
#endif // ENABLE_WALLET
static void masternode_status_help()
static void masternode_status_help(const JSONRPCRequest& request)
{
throw std::runtime_error(
RPCHelpMan{"masternode status",
"Print masternode status information\n",
{},
RPCResults{},
RPCExamples{""}
}.ToString());
}.Check(request);
}
static UniValue masternode_status(const JSONRPCRequest& request)
{
if (request.fHelp)
masternode_status_help();
masternode_status_help(request);
if (!fMasternodeMode)
throw JSONRPCError(RPC_INTERNAL_ERROR, "This is not a masternode");
@ -286,12 +277,12 @@ static std::string GetRequiredPaymentsString(int nBlockHeight, const CDeterminis
if (payee) {
CTxDestination dest;
if (!ExtractDestination(payee->pdmnState->scriptPayout, dest)) {
assert(false);
CHECK_NONFATAL(false);
}
strPayments = EncodeDestination(dest);
if (payee->nOperatorReward != 0 && payee->pdmnState->scriptOperatorPayout != CScript()) {
if (!ExtractDestination(payee->pdmnState->scriptOperatorPayout, dest)) {
assert(false);
CHECK_NONFATAL(false);
}
strPayments += ", " + EncodeDestination(dest);
}
@ -316,9 +307,8 @@ static std::string GetRequiredPaymentsString(int nBlockHeight, const CDeterminis
return strPayments;
}
static void masternode_winners_help()
static void masternode_winners_help(const JSONRPCRequest& request)
{
throw std::runtime_error(
RPCHelpMan{"masternode winners",
"Print list of masternode winners\n",
{
@ -327,13 +317,13 @@ static void masternode_winners_help()
},
RPCResults{},
RPCExamples{""}
}.ToString());
}.Check(request);
}
static UniValue masternode_winners(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 3)
masternode_winners_help();
masternode_winners_help(request);
const CBlockIndex* pindexTip{nullptr};
{
@ -375,9 +365,8 @@ static UniValue masternode_winners(const JSONRPCRequest& request)
return obj;
}
static void masternode_payments_help()
static void masternode_payments_help(const JSONRPCRequest& request)
{
throw std::runtime_error(
RPCHelpMan{"masternode payments",
"\nReturns an array of deterministic masternodes and their payments for the specified block\n",
{
@ -385,16 +374,16 @@ static void masternode_payments_help()
{"count", RPCArg::Type::NUM, /* default */ "1", "The number of blocks to return. Will return <count> previous blocks if <count> is negative. Both 1 and -1 correspond to the chain tip."},
},
RPCResult {
" [ (array) Blocks\n"
" [ (json array) Blocks\n"
" {\n"
" \"height\" : n, (numeric) The height of the block\n"
" \"blockhash\" : \"hash\", (string) The hash of the block\n"
" \"amount\" : n (numeric) Amount received in this block by all masternodes\n"
" \"masternodes\": [ (array) Masternodes that received payments in this block\n"
" \"masternodes\" : [ (json array) Masternodes that received payments in this block\n"
" {\n"
" \"proTxHash\" : \"xxxx\", (string) The hash of the corresponding ProRegTx\n"
" \"amount\" : n (numeric) Amount received by this masternode\n"
" \"payees\": [ (array) Payees who received a share of this payment\n"
" \"payees\" : [ (json array) Payees who received a share of this payment\n"
" {\n"
" \"address\" : \"xxx\", (string) Payee address\n"
" \"script\" : \"xxx\", (string) Payee scriptPubKey\n"
@ -407,13 +396,13 @@ static void masternode_payments_help()
" ]"
},
RPCExamples{""}
}.ToString());
}.Check(request);
}
static UniValue masternode_payments(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 3) {
masternode_payments_help();
masternode_payments_help(request);
}
CBlockIndex* pindex{nullptr};
@ -522,7 +511,6 @@ static UniValue masternode_payments(const JSONRPCRequest& request)
[[ noreturn ]] static void masternode_help()
{
throw std::runtime_error(
RPCHelpMan{"masternode",
"Set of commands to execute masternode related actions\n"
"\nAvailable commands:\n"
@ -541,7 +529,7 @@ static UniValue masternode_payments(const JSONRPCRequest& request)
},
RPCResults{},
RPCExamples{""},
}.ToString());
}.Throw();
}
static UniValue masternode(const JSONRPCRequest& request)
@ -597,7 +585,7 @@ static UniValue masternodelist(const JSONRPCRequest& request)
strMode != "payee" && strMode != "pubkeyoperator" &&
strMode != "status"))
{
masternode_list_help();
masternode_list_help(request);
}
UniValue obj(UniValue::VOBJ);

View File

@ -93,8 +93,7 @@ static UniValue getnetworkhashps(const JSONRPCRequest& request)
{"height", RPCArg::Type::NUM, /* default */ "-1", "To estimate at the time of the given height."},
},
RPCResult{
"x (numeric) Hashes per second estimated\n"
},
RPCResult::Type::NUM, "", "Hashes per second estimated"},
RPCExamples{
HelpExampleCli("getnetworkhashps", "")
+ HelpExampleRpc("getnetworkhashps", "")
@ -166,8 +165,10 @@ static UniValue generatetoaddress(const JSONRPCRequest& request)
{"maxtries", RPCArg::Type::NUM, /* default */ "1000000", "How many iterations to try."},
},
RPCResult{
"[ blockhashes ] (array) hashes of blocks generated\n"
},
RPCResult::Type::ARR, "", "hashes of blocks generated",
{
{RPCResult::Type::STR_HEX, "", "blockhash"},
}},
RPCExamples{
"\nGenerate 11 blocks to myaddress\n"
+ HelpExampleCli("generatetoaddress", "11 \"myaddress\"")
@ -253,8 +254,7 @@ static UniValue prioritisetransaction(const JSONRPCRequest& request)
" considers the transaction as it would have paid a higher (or lower) fee."},
},
RPCResult{
"true (boolean) Returns true\n"
},
RPCResult::Type::BOOL, "", "Returns true"},
RPCExamples{
HelpExampleCli("prioritisetransaction", "\"txid\" 10000")
+ HelpExampleRpc("prioritisetransaction", "\"txid\", 10000")
@ -338,11 +338,11 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
" },\n"
" \"vbrequired\" : n, (numeric) bit mask of versionbits the server requires set in submissions\n"
" \"previousblockhash\" : \"xxxx\", (string) The hash of current highest block\n"
" \"transactions\" : [ (array) contents of non-coinbase transactions that should be included in the next block\n"
" \"transactions\" : [ (json array) contents of non-coinbase transactions that should be included in the next block\n"
" {\n"
" \"data\" : \"xxxx\", (string) transaction data encoded in hexadecimal (byte-for-byte)\n"
" \"hash\" : \"xxxx\", (string) hash/id encoded in little-endian hexadecimal\n"
" \"depends\" : [ (array) array of numbers \n"
" \"depends\" : [ (json array) array of numbers \n"
" n (numeric) transactions before this one (by 1-based index in 'transactions' list) that must be present in the final block if this one is\n"
" ,...\n"
" ],\n"
@ -367,7 +367,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
" \"bits\" : \"xxxxxxxx\", (string) compressed target of next block\n"
" \"previousbits\" : \"xxxxxxxx\", (string) compressed target of current highest block\n"
" \"height\" : n (numeric) The height of the next block\n"
" \"masternode\" : [ (array) required masternode payments that must be included in the next block\n"
" \"masternode\" : [ (json array) required masternode payments that must be included in the next block\n"
" {\n"
" \"payee\" : \"xxxx\", (string) payee address\n"
" \"script\" : \"xxxx\", (string) payee scriptPubKey\n"
@ -376,7 +376,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
" },\n"
" \"masternode_payments_started\" : true|false, (boolean) true, if masternode payments started\n"
" \"masternode_payments_enforced\" : true|false, (boolean) true, if masternode payments are enforced\n"
" \"superblock\" : [ (array) required superblock payees that must be included in the next block\n"
" \"superblock\" : [ (json array) required superblock payees that must be included in the next block\n"
" {\n"
" \"payee\" : \"xxxx\", (string) payee address\n"
" \"script\" : \"xxxx\", (string) payee scriptPubKey\n"
@ -549,7 +549,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
// Need to update only after we know CreateNewBlock succeeded
pindexPrev = pindexPrevNew;
}
assert(pindexPrev);
CHECK_NONFATAL(pindexPrev);
CBlock* pblock = &pblocktemplate->block; // pointer for convenience
const Consensus::Params& consensusParams = Params().GetConsensus();
@ -798,8 +798,7 @@ static UniValue submitheader(const JSONRPCRequest& request)
{"hexdata", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "the hex-encoded block header data"},
},
RPCResult{
"None"
},
RPCResult::Type::NONE, "", "None"},
RPCExamples{
HelpExampleCli("submitheader", "\"aabbcc\"") +
HelpExampleRpc("submitheader", "\"aabbcc\"")
@ -848,17 +847,19 @@ static UniValue estimatesmartfee(const JSONRPCRequest& request)
" \"CONSERVATIVE\""},
},
RPCResult{
"{\n"
" \"feerate\" : x.x, (numeric, optional) estimate fee rate in " + CURRENCY_UNIT + "/kB\n"
" \"errors\": [ str... ] (json array of strings, optional) Errors encountered during processing\n"
" \"blocks\" : n (numeric) block number where estimate was found\n"
"}\n"
"\n"
RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::NUM, "feerate", /* optional */ true, "estimate fee rate in " + CURRENCY_UNIT + "/kB (only present if no errors were encountered)"},
{RPCResult::Type::ARR, "errors", "Errors encountered during processing",
{
{RPCResult::Type::STR, "", "error"},
}},
{RPCResult::Type::NUM, "blocks", "block number where estimate was found\n"
"The request target will be clamped between 2 and the highest target\n"
"fee estimation is able to return based on how long it has been running.\n"
"An error is returned if not enough transactions and blocks\n"
"have been observed to make an estimate for any number of blocks.\n"
},
"have been observed to make an estimate for any number of blocks."},
}},
RPCExamples{
HelpExampleCli("estimatesmartfee", "6")
},

View File

@ -15,8 +15,9 @@
#include <rpc/util.h>
#include <script/descriptor.h>
#include <txmempool.h>
#include <util/system.h>
#include <util/check.h>
#include <util/strencodings.h>
#include <util/system.h>
#include <validation.h>
#include <util/validation.h>
@ -302,13 +303,14 @@ UniValue getdescriptorinfo(const JSONRPCRequest& request)
{"descriptor", RPCArg::Type::STR, RPCArg::Optional::NO, "The descriptor"},
},
RPCResult{
"{\n"
" \"descriptor\" : \"desc\", (string) The descriptor in canonical form, without private keys\n"
" \"checksum\" : \"chksum\", (string) The checksum for the input descriptor\n"
" \"isrange\" : true|false, (boolean) Whether the descriptor is ranged\n"
" \"issolvable\" : true|false, (boolean) Whether the descriptor is solvable\n"
" \"hasprivatekeys\" : true|false, (boolean) Whether the input descriptor contained at least one private key\n"
"}\n"
RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::STR, "descriptor", "The descriptor in canonical form, without private keys"},
{RPCResult::Type::STR, "checksum", "The checksum for the input descriptor"},
{RPCResult::Type::BOOL, "isrange", "Whether the descriptor is ranged"},
{RPCResult::Type::BOOL, "issolvable", "Whether the descriptor is solvable"},
{RPCResult::Type::BOOL, "hasprivatekeys", "Whether the input descriptor contained at least one private key"},
}
},
RPCExamples{
"\nAnalyse a descriptor\n"
@ -353,7 +355,7 @@ UniValue deriveaddresses(const JSONRPCRequest& request)
{"range", RPCArg::Type::RANGE, RPCArg::Optional::OMITTED_NAMED_ARG, "If a ranged descriptor is used, this specifies the end or the range (in [begin,end] notation) to derive."},
},
RPCResult{
"\"address\" (array) A json array of the derived addresses\n"
"\"address\" (json array) A json array of the derived addresses\n"
" [\n"
" ...\n"
" ]\n"
@ -429,7 +431,7 @@ static UniValue verifymessage(const JSONRPCRequest& request)
{"message", RPCArg::Type::STR, RPCArg::Optional::NO, "The message that was signed."},
},
RPCResult{
"true|false (boolean) If the signature is verified or not.\n"
RPCResult::Type::BOOL, "", "If the signature is verified or not."
},
RPCExamples{
"\nUnlock the wallet for 30 seconds\n"
@ -487,7 +489,7 @@ static UniValue signmessagewithprivkey(const JSONRPCRequest& request)
{"message", RPCArg::Type::STR, RPCArg::Optional::NO, "The message to create a signature of."},
},
RPCResult{
"\"signature\" (string) The signature of the message encoded in base 64\n"
RPCResult::Type::STR, "signature", "The signature of the message encoded in base 64"
},
RPCExamples{
"\nCreate the signature\n"
@ -1136,19 +1138,21 @@ static UniValue getmemoryinfo(const JSONRPCRequest& request)
},
{
RPCResult{"mode \"stats\"",
"{\n"
" \"locked\": { (json object) Information about locked memory manager\n"
" \"used\": xxxxx, (numeric) Number of bytes used\n"
" \"free\": xxxxx, (numeric) Number of bytes available in current arenas\n"
" \"total\": xxxxxxx, (numeric) Total number of bytes managed\n"
" \"locked\": xxxxxx, (numeric) Amount of bytes that succeeded locking. If this number is smaller than total, locking pages failed at some point and key data could be swapped to disk.\n"
" \"chunks_used\": xxxxx, (numeric) Number allocated chunks\n"
" \"chunks_free\": xxxxx, (numeric) Number unused chunks\n"
" }\n"
"}\n"
RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::OBJ, "locked", "Information about locked memory manager",
{
{RPCResult::Type::NUM, "used", "Number of bytes used"},
{RPCResult::Type::NUM, "free", "Number of bytes available in current arenas"},
{RPCResult::Type::NUM, "total", "Total number of bytes managed"},
{RPCResult::Type::NUM, "locked", "Amount of bytes that succeeded locking. If this number is smaller than total, locking pages failed at some point and key data could be swapped to disk."},
{RPCResult::Type::NUM, "chunks_used", "Number allocated chunks"},
{RPCResult::Type::NUM, "chunks_free", "Number unused chunks"},
}},
}
},
RPCResult{"mode \"mallocinfo\"",
"\"<malloc version=\"1\">...\"\n"
RPCResult::Type::STR, "", "\"<malloc version=\"1\">...\""
},
},
RPCExamples{
@ -1219,10 +1223,10 @@ static UniValue logging(const JSONRPCRequest& request)
}},
},
RPCResult{
"{ (json object where keys are the logging categories, and values indicates its status\n"
" \"category\": true|false, (bool) if being debug logged or not. false:inactive, true:active\n"
" ...\n"
"}\n"
RPCResult::Type::OBJ_DYN, "", "keys are the logging categories, and values indicates its status",
{
{RPCResult::Type::BOOL, "category", "if being debug logged or not. false:inactive, true:active"},
}
},
RPCExamples{
HelpExampleCli("logging", "\"[\\\"all\\\"]\" \"[\\\"http\\\"]\"")
@ -1270,6 +1274,7 @@ static UniValue echo(const JSONRPCRequest& request)
throw std::runtime_error(
RPCHelpMan{"echo|echojson ...",
"\nSimply echo back the input arguments. This command is for testing.\n"
"\nIt will return an internal bug report when exactly 100 arguments are passed.\n"
"\nThe difference between echo and echojson is that echojson has argument conversion enabled in the client-side table in "
"dash-cli and the GUI. There is no server-side difference.",
{},
@ -1278,6 +1283,8 @@ static UniValue echo(const JSONRPCRequest& request)
}.ToString()
);
CHECK_NONFATAL(request.params.size() != 100);
return request.params;
}

View File

@ -34,7 +34,7 @@ static UniValue getconnectioncount(const JSONRPCRequest& request)
"\nReturns the number of connections to other nodes.\n",
{},
RPCResult{
"n (numeric) The connection count\n"
RPCResult::Type::NUM, "", "The connection count"
},
RPCExamples{
HelpExampleCli("getconnectioncount", "")
@ -96,7 +96,7 @@ static UniValue getpeerinfo(const JSONRPCRequest& request)
" \"verified_pubkey_hash\" : h, (hex) Only present when the peer is a masternode and successfully\n"
" authenticated via MNAUTH. In this case, this field contains the\n"
" hash of the masternode's operator public key\n"
" \"servicesnames\":[ (array) the services offered, in human-readable form\n"
" \"servicesnames\" : [ (json array) the services offered, in human-readable form\n"
" \"SERVICE_NAME\", (string) the service name if it is recognised\n"
" ...\n"
" ],\n"
@ -413,20 +413,21 @@ static UniValue getnettotals(const JSONRPCRequest& request)
"and current time.\n",
{},
RPCResult{
"{\n"
" \"totalbytesrecv\": n, (numeric) Total bytes received\n"
" \"totalbytessent\": n, (numeric) Total bytes sent\n"
" \"timemillis\": t, (numeric) Current UNIX time in milliseconds\n"
" \"uploadtarget\":\n"
" {\n"
" \"timeframe\": n, (numeric) Length of the measuring timeframe in seconds\n"
" \"target\": n, (numeric) Target in bytes\n"
" \"target_reached\": true|false, (boolean) True if target is reached\n"
" \"serve_historical_blocks\": true|false, (boolean) True if serving historical blocks\n"
" \"bytes_left_in_cycle\": t, (numeric) Bytes left in current time cycle\n"
" \"time_left_in_cycle\": t (numeric) Seconds left in current time cycle\n"
" }\n"
"}\n"
RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::NUM, "totalbytesrecv", "Total bytes received"},
{RPCResult::Type::NUM, "totalbytessent", "Total bytes sent"},
{RPCResult::Type::NUM_TIME, "timemillis", "Current UNIX time in milliseconds"},
{RPCResult::Type::OBJ, "uploadtarget", "",
{
{RPCResult::Type::NUM, "timeframe", "Length of the measuring timeframe in seconds"},
{RPCResult::Type::NUM, "target", "Target in bytes"},
{RPCResult::Type::BOOL, "target_reached", "True if target is reached"},
{RPCResult::Type::BOOL, "serve_historical_blocks", "True if serving historical blocks"},
{RPCResult::Type::NUM, "bytes_left_in_cycle", "Bytes left in current time cycle"},
{RPCResult::Type::NUM, "time_left_in_cycle", "Seconds left in current time cycle"},
}},
}
},
RPCExamples{
HelpExampleCli("getnettotals", "")
@ -487,16 +488,16 @@ static UniValue getnetworkinfo(const JSONRPCRequest& request)
" \"subversion\" : \"/Dash Core:x.x.x.x/\", (string) the server subversion string\n"
" \"protocolversion\" : xxxxx, (numeric) the protocol version\n"
" \"localservices\" : \"xxxxxxxxxxxxxxxx\", (string) the services we offer to the network\n"
" \"localservicesnames\": [ (array) the services we offer to the network, in human-readable form\n"
" \"localservicesnames\" : [ (json array) the services we offer to the network, in human-readable form\n"
" \"SERVICE_NAME\", (string) the service name\n"
" ...\n"
" ],\n"
" \"localrelay\": true|false, (bool) true if transaction relay is requested from peers\n"
" \"localrelay\" : true|false, (boolean) true if transaction relay is requested from peers\n"
" \"timeoffset\" : xxxxx, (numeric) the time offset\n"
" \"connections\" : xxxxx, (numeric) the number of connections\n"
" \"networkactive\": true|false, (bool) whether p2p networking is enabled\n"
" \"networkactive\" : true|false, (boolean) whether p2p networking is enabled\n"
" \"socketevents\" : \"xxx/\", (string) the socket events mode, either kqueue, epoll, poll or select\n"
" \"networks\": [ (array) information per network\n"
" \"networks\" : [ (json array) information per network\n"
" {\n"
" \"name\" : \"xxx\", (string) network (ipv4, ipv6 or onion)\n"
" \"limited\" : true|false, (boolean) is the network limited using -onlynet?\n"
@ -508,7 +509,7 @@ static UniValue getnetworkinfo(const JSONRPCRequest& request)
" ],\n"
" \"relayfee\" : x.xxxxxxxx, (numeric) minimum relay fee for transactions in " + CURRENCY_UNIT + "/kB\n"
" \"incrementalfee\" : x.xxxxxxxx, (numeric) minimum fee increment for mempool limiting in " + CURRENCY_UNIT + "/kB\n"
" \"localaddresses\": [ (array) list of local addresses\n"
" \"localaddresses\" : [ (json array) list of local addresses\n"
" {\n"
" \"address\" : \"xxxx\", (string) network address\n"
" \"port\" : xxx, (numeric) network port\n"
@ -556,7 +557,7 @@ static UniValue getnetworkinfo(const JSONRPCRequest& request)
strSocketEvents = "kqueue";
break;
default:
assert(false);
CHECK_NONFATAL(false);
}
obj.pushKV("socketevents", strSocketEvents);
}

View File

@ -6,15 +6,6 @@
#ifndef BITCOIN_RPC_PROTOCOL_H
#define BITCOIN_RPC_PROTOCOL_H
#include <fs.h>
#include <list>
#include <map>
#include <stdint.h>
#include <string>
#include <univalue.h>
//! HTTP status codes
enum HTTPStatusCode
{
@ -93,18 +84,4 @@ enum RPCErrorCode
RPC_FORBIDDEN_BY_SAFE_MODE = -2, //!< Server is in safe mode, and command is not allowed in safe mode
};
UniValue JSONRPCRequestObj(const std::string& strMethod, const UniValue& params, const UniValue& id);
UniValue JSONRPCReplyObj(const UniValue& result, const UniValue& error, const UniValue& id);
std::string JSONRPCReply(const UniValue& result, const UniValue& error, const UniValue& id);
UniValue JSONRPCError(int code, const std::string& message);
/** Generate a new RPC authentication cookie and write it to disk */
bool GenerateAuthCookie(std::string *cookie_out);
/** Read the RPC authentication cookie from disk */
bool GetAuthCookie(std::string *cookie_out);
/** Delete RPC authentication cookie from disk */
void DeleteAuthCookie();
/** Parse JSON-RPC batch reply into a vector */
std::vector<UniValue> JSONRPCProcessBatchReply(const UniValue &in, size_t num);
#endif // BITCOIN_RPC_PROTOCOL_H

View File

@ -133,11 +133,11 @@ static UniValue getrawtransaction(const JSONRPCRequest& request)
},
{
RPCResult{"if verbose is not set or set to false",
"\"data\" (string) The serialized, hex-encoded data for 'txid'\n"
RPCResult::Type::STR, "data", "The serialized, hex-encoded data for 'txid'"
},
RPCResult{"if verbose is set to true",
"{\n"
" \"in_active_chain\": b, (bool) Whether specified block is in the active chain or not (only present with explicit \"blockhash\" argument)\n"
" \"in_active_chain\" : b, (boolean) Whether specified block is in the active chain or not (only present with explicit \"blockhash\" argument)\n"
" \"txid\" : \"id\", (string) The transaction id (same as provided)\n"
" \"size\" : n, (numeric) The transaction size\n"
" \"version\" : n, (numeric) The version\n"
@ -179,9 +179,9 @@ static UniValue getrawtransaction(const JSONRPCRequest& request)
" \"confirmations\" : n, (numeric) The confirmations\n"
" \"blocktime\" : ttt (numeric) The block time in seconds since epoch (Jan 1 1970 GMT)\n"
" \"time\" : ttt, (numeric) Same as \"blocktime\"\n"
" \"instantlock\" : true|false, (bool) Current transaction lock state\n"
" \"instantlock_internal\" : true|false, (bool) Current internal transaction lock state\n"
" \"chainlock\" : true|false, (bool) The state of the corresponding block chainlock\n"
" \"instantlock\" : true|false, (boolean) Current transaction lock state\n"
" \"instantlock_internal\" : true|false, (boolean) Current internal transaction lock state\n"
" \"chainlock\" : true|false, (boolean) The state of the corresponding block chainlock\n"
"}\n"
},
},
@ -273,7 +273,7 @@ static UniValue gettxoutproof(const JSONRPCRequest& request)
{"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED_NAMED_ARG, "If specified, looks for txid in the block with this hash"},
},
RPCResult{
"\"data\" (string) A string that is a serialized, hex-encoded data for the proof.\n"
RPCResult::Type::STR, "data", "A string that is a serialized, hex-encoded data for the proof."
},
RPCExamples{
HelpExampleCli("gettxoutproof", "'[\"mytxid\",...]'")
@ -367,7 +367,10 @@ static UniValue verifytxoutproof(const JSONRPCRequest& request)
{"proof", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex-encoded proof generated by gettxoutproof"},
},
RPCResult{
"[\"txid\"] (array, strings) The txid(s) which the proof commits to, or empty array if the proof can not be validated.\n"
RPCResult::Type::ARR, "", "",
{
{RPCResult::Type::STR_HEX, "txid", "The txid(s) which the proof commits to, or empty array if the proof can not be validated."},
}
},
RPCExamples{
HelpExampleCli("verifytxoutproof", "\"proof\"")
@ -446,7 +449,7 @@ static UniValue createrawtransaction(const JSONRPCRequest& request)
{"locktime", RPCArg::Type::NUM, /* default */ "0", "Raw locktime. Non-0 value also locktime-activates inputs"},
},
RPCResult{
"\"transaction\" (string) hex string of the transaction\n"
RPCResult::Type::STR_HEX, "transaction", "hex string of the transaction"
},
RPCExamples{
HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"address\\\":0.01}]\"")
@ -617,7 +620,7 @@ static UniValue combinerawtransaction(const JSONRPCRequest& request)
},
},
RPCResult{
"\"hex\" (string) The hex-encoded raw transaction with signature(s)\n"
RPCResult::Type::STR, "", "The hex-encoded raw transaction with signature(s)"
},
RPCExamples{
HelpExampleCli("combinerawtransaction", "'[\"myhex1\", \"myhex2\", \"myhex3\"]'")
@ -725,20 +728,22 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
},
},
RPCResult{
"{\n"
" \"hex\" : \"value\", (string) The hex-encoded raw transaction with signature(s)\n"
" \"complete\" : true|false, (boolean) If the transaction has a complete set of signatures\n"
" \"errors\" : [ (json array of objects) Script verification errors (if there are any)\n"
" {\n"
" \"txid\" : \"hash\", (string) The hash of the referenced, previous transaction\n"
" \"vout\" : n, (numeric) The index of the output to spent and used as input\n"
" \"scriptSig\" : \"hex\", (string) The hex-encoded signature script\n"
" \"sequence\" : n, (numeric) Script sequence number\n"
" \"error\" : \"text\" (string) Verification or signing error related to the input\n"
" }\n"
" ,...\n"
" ]\n"
"}\n"
RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::STR_HEX, "hex", "The hex-encoded raw transaction with signature(s)"},
{RPCResult::Type::BOOL, "complete", "If the transaction has a complete set of signatures"},
{RPCResult::Type::ARR, "errors", "Script verification errors (if there are any)",
{
{RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::STR_HEX, "txid", "The hash of the referenced, previous transaction"},
{RPCResult::Type::NUM, "vout", "The index of the output to spent and used as input"},
{RPCResult::Type::STR_HEX, "scriptSig", "The hex-encoded signature script"},
{RPCResult::Type::NUM, "sequence", "Script sequence number"},
{RPCResult::Type::STR, "error", "Verification or signing error related to the input"},
}},
}},
}
},
RPCExamples{
HelpExampleCli("signrawtransactionwithkey", "\"myhex\" \"[\\\"key1\\\",\\\"key2\\\"]\"")
@ -788,7 +793,7 @@ UniValue sendrawtransaction(const JSONRPCRequest& request)
{"bypasslimits", RPCArg::Type::BOOL, /* default_val */ "false", "Bypass transaction policy limits"},
},
RPCResult{
"\"hex\" (string) The transaction hash in hex\n"
RPCResult::Type::STR_HEX, "", "The transaction hash in hex"
},
RPCExamples{
"\nCreate a transaction\n"
@ -856,14 +861,16 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request)
{"maxfeerate", RPCArg::Type::AMOUNT, /* default */ FormatMoney(DEFAULT_MAX_RAW_TX_FEE), "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT + "/kB\n"},
},
RPCResult{
"[ (array) The result of the mempool acceptance test for each raw transaction in the input array.\n"
" Length is exactly one for now.\n"
" {\n"
" \"txid\" (string) The transaction hash in hex\n"
" \"allowed\" (boolean) If the mempool allows this tx to be inserted\n"
" \"reject-reason\" (string) Rejection string (only present when 'allowed' is false)\n"
" }\n"
"]\n"
RPCResult::Type::ARR, "", "The result of the mempool acceptance test for each raw transaction in the input array.\n"
"Length is exactly one for now.",
{
{RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"},
{RPCResult::Type::BOOL, "allowed", "If the mempool allows this tx to be inserted"},
{RPCResult::Type::STR, "reject-reason", "Rejection string (only present when 'allowed' is false)"},
}},
}
},
RPCExamples{
"\nCreate a transaction\n"
@ -1195,7 +1202,7 @@ UniValue combinepsbt(const JSONRPCRequest& request)
},
},
RPCResult{
" \"psbt\" (string) The base64-encoded partially signed transaction\n"
RPCResult::Type::STR, "", "The base64-encoded partially signed transaction"
},
RPCExamples{
HelpExampleCli("combinepsbt", "[\"mybase64_1\", \"mybase64_2\", \"mybase64_3\"]")
@ -1328,7 +1335,7 @@ UniValue createpsbt(const JSONRPCRequest& request)
{"locktime", RPCArg::Type::NUM, /* default */ "0", "Raw locktime. Non-0 value also locktime-activates inputs"},
},
RPCResult{
" \"psbt\" (string) The resulting raw transaction (base64-encoded string)\n"
RPCResult::Type::STR, "", "The resulting raw transaction (base64-encoded string)"
},
RPCExamples{
HelpExampleCli("createpsbt", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"data\\\":\\\"00010203\\\"}]\"")
@ -1375,7 +1382,7 @@ UniValue converttopsbt(const JSONRPCRequest& request)
" will continue. If false, RPC will fail if any signatures are present."},
},
RPCResult{
" \"psbt\" (string) The resulting raw transaction (base64-encoded string)\n"
RPCResult::Type::STR, "", "The resulting raw transaction (base64-encoded string)"
},
RPCExamples{
"\nCreate a transaction\n"
@ -1430,7 +1437,7 @@ UniValue utxoupdatepsbt(const JSONRPCRequest& request)
{"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "A base64 string of a PSBT"}
},
RPCResult {
" \"psbt\" (string) The base64-encoded partially signed transaction with inputs updated\n"
RPCResult::Type::STR, "", "The base64-encoded partially signed transaction with inputs updated"
},
RPCExamples {
HelpExampleCli("utxoupdatepsbt", "\"psbt\"")
@ -1495,7 +1502,7 @@ UniValue joinpsbts(const JSONRPCRequest& request)
}}
},
RPCResult {
" \"psbt\" (string) The base64-encoded partially signed transaction\n"
RPCResult::Type::STR, "", "The base64-encoded partially signed transaction"
},
RPCExamples {
HelpExampleCli("joinpsbts", "\"psbt\"")

View File

@ -12,7 +12,7 @@
#include <keystore.h>
#include <policy/policy.h>
#include <primitives/transaction.h>
#include <rpc/protocol.h>
#include <rpc/request.h>
#include <rpc/util.h>
#include <univalue.h>
#include <util/strencodings.h>

View File

@ -1,15 +1,16 @@
// Copyright (c) 2010 Satoshi Nakamoto
// Copyright (c) 2009-2015 The Bitcoin Core developers
// Copyright (c) 2014-2020 The Dash Core developers
// Copyright (c) 2009-2019 The Bitcoin Core developers
// Copyright (c) 2014-2022 The Dash Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <rpc/protocol.h>
#include <rpc/request.h>
#include <fs.h>
#include <random.h>
#include <rpc/protocol.h>
#include <util/system.h>
#include <util/strencodings.h>
#include <util/time.h>
/**
* JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility,
@ -148,3 +149,38 @@ std::vector<UniValue> JSONRPCProcessBatchReply(const UniValue &in, size_t num)
}
return batch;
}
void JSONRPCRequest::parse(const UniValue& valRequest)
{
// Parse request
if (!valRequest.isObject())
throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object");
const UniValue& request = valRequest.get_obj();
// Parse id now so errors from here on will have the id
id = find_value(request, "id");
// Parse method
UniValue valMethod = find_value(request, "method");
if (valMethod.isNull())
throw JSONRPCError(RPC_INVALID_REQUEST, "Missing method");
if (!valMethod.isStr())
throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string");
strMethod = valMethod.get_str();
if (strMethod != "getblocktemplate") {
if (fLogIPs)
LogPrint(BCLog::RPC, "ThreadRPCServer method=%s user=%s peeraddr=%s\n", SanitizeString(strMethod),
this->authUser, this->peerAddr);
else
LogPrint(BCLog::RPC, "ThreadRPCServer method=%s user=%s\n", SanitizeString(strMethod), this->authUser);
}
// Parse params
UniValue valParams = find_value(request, "params");
if (valParams.isArray() || valParams.isObject())
params = valParams;
else if (valParams.isNull())
params = UniValue(UniValue::VARR);
else
throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array or object");
}

42
src/rpc/request.h Normal file
View File

@ -0,0 +1,42 @@
// Copyright (c) 2010 Satoshi Nakamoto
// Copyright (c) 2009-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_RPC_REQUEST_H
#define BITCOIN_RPC_REQUEST_H
#include <string>
#include <univalue.h>
UniValue JSONRPCRequestObj(const std::string& strMethod, const UniValue& params, const UniValue& id);
UniValue JSONRPCReplyObj(const UniValue& result, const UniValue& error, const UniValue& id);
std::string JSONRPCReply(const UniValue& result, const UniValue& error, const UniValue& id);
UniValue JSONRPCError(int code, const std::string& message);
/** Generate a new RPC authentication cookie and write it to disk */
bool GenerateAuthCookie(std::string *cookie_out);
/** Read the RPC authentication cookie from disk */
bool GetAuthCookie(std::string *cookie_out);
/** Delete RPC authentication cookie from disk */
void DeleteAuthCookie();
/** Parse JSON-RPC batch reply into a vector */
std::vector<UniValue> JSONRPCProcessBatchReply(const UniValue &in, size_t num);
class JSONRPCRequest
{
public:
UniValue id;
std::string strMethod;
UniValue params;
bool fHelp;
std::string URI;
std::string authUser;
std::string peerAddr;
JSONRPCRequest() : id(NullUniValue), params(NullUniValue), fHelp(false) {}
void parse(const UniValue& valRequest);
};
#endif // BITCOIN_RPC_REQUEST_H

View File

@ -175,7 +175,7 @@ static CBLSSecretKey ParseBLSSecretKey(const std::string& hexKey, const std::str
template<typename SpecialTxPayload>
static void FundSpecialTx(CWallet* pwallet, CMutableTransaction& tx, const SpecialTxPayload& payload, const CTxDestination& fundDest)
{
assert(pwallet != nullptr);
CHECK_NONFATAL(pwallet != nullptr);
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
@ -242,7 +242,7 @@ static void FundSpecialTx(CWallet* pwallet, CMutableTransaction& tx, const Speci
// CreateTransaction added a change output, so we don't need the dummy txout anymore.
// Removing it results in slight overpayment of fees, but we ignore this for now (as it's a very low amount).
auto it = std::find(tx.vout.begin(), tx.vout.end(), dummyTxOut);
assert(it != tx.vout.end());
CHECK_NONFATAL(it != tx.vout.end());
tx.vout.erase(it);
}
}
@ -315,16 +315,15 @@ static std::string SignAndSendSpecialTx(const CMutableTransaction& tx, bool fSub
return sendrawtransaction(sendRequest).get_str();
}
static void protx_register_fund_help(CWallet* const pwallet)
static void protx_register_fund_help(const JSONRPCRequest& request)
{
throw std::runtime_error(
RPCHelpMan{"protx register_fund",
"\nCreates, funds and sends a ProTx to the network. The resulting transaction will move 1000 Dash\n"
"to the address specified by collateralAddress and will then function as the collateral of your\n"
"masternode.\n"
"A few of the limitations you see in the arguments are temporary and might be lifted after DIP3\n"
"is fully deployed.\n"
+ HelpRequiringPassphrase(pwallet) + "\n",
+ HelpRequiringPassphrase() + "\n",
{
GetRpcArg("collateralAddress"),
GetRpcArg("ipAndPort"),
@ -345,17 +344,16 @@ static void protx_register_fund_help(CWallet* const pwallet)
RPCExamples{
HelpExampleCli("protx", "register_fund \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\" \"1.2.3.4:1234\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" \"93746e8731c57f87f79b3620a7982924e2931717d49540a85864bd543de11c43fb868fd63e501a1db37e19ed59ae6db4\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" 0 \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\"")
},
}.ToString());
}.Check(request);
}
static void protx_register_help(CWallet* const pwallet)
static void protx_register_help(const JSONRPCRequest& request)
{
throw std::runtime_error(
RPCHelpMan{"protx register",
"\nSame as \"protx register_fund\", but with an externally referenced collateral.\n"
"The collateral is specified through \"collateralHash\" and \"collateralIndex\" and must be an unspent\n"
"transaction output spendable by this wallet. It must also not be used by any other masternode.\n"
+ HelpRequiringPassphrase(pwallet) + "\n",
+ HelpRequiringPassphrase() + "\n",
{
GetRpcArg("collateralHash"),
GetRpcArg("collateralIndex"),
@ -377,12 +375,11 @@ static void protx_register_help(CWallet* const pwallet)
RPCExamples{
HelpExampleCli("protx", "register \"0123456701234567012345670123456701234567012345670123456701234567\" 0 \"1.2.3.4:1234\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" \"93746e8731c57f87f79b3620a7982924e2931717d49540a85864bd543de11c43fb868fd63e501a1db37e19ed59ae6db4\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" 0 \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\"")
},
}.ToString());
}.Check(request);
}
static void protx_register_prepare_help()
static void protx_register_prepare_help(const JSONRPCRequest& request)
{
throw std::runtime_error(
RPCHelpMan{"protx register_prepare",
"\nCreates an unsigned ProTx and a message that must be signed externally\n"
"with the private key that corresponds to collateralAddress to prove collateral ownership.\n"
@ -409,17 +406,16 @@ static void protx_register_prepare_help()
RPCExamples{
HelpExampleCli("protx", "register_prepare \"0123456701234567012345670123456701234567012345670123456701234567\" 0 \"1.2.3.4:1234\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" \"93746e8731c57f87f79b3620a7982924e2931717d49540a85864bd543de11c43fb868fd63e501a1db37e19ed59ae6db4\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" 0 \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\"")
},
}.ToString());
}.Check(request);
}
static void protx_register_submit_help(CWallet* const pwallet)
static void protx_register_submit_help(const JSONRPCRequest& request)
{
throw std::runtime_error(
RPCHelpMan{"protx register_submit",
"\nCombines the unsigned ProTx and a signature of the signMessage, signs all inputs\n"
"which were added to cover fees and submits the resulting transaction to the network.\n"
"Note: See \"help protx register_prepare\" for more info about creating a ProTx and a message to sign.\n"
+ HelpRequiringPassphrase(pwallet) + "\n",
+ HelpRequiringPassphrase() + "\n",
{
{"tx", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The serialized unsigned ProTx in hex format."},
{"sig", RPCArg::Type::STR, RPCArg::Optional::NO, "The signature signed with the collateral key. Must be in base64 format."},
@ -430,29 +426,28 @@ static void protx_register_submit_help(CWallet* const pwallet)
RPCExamples{
HelpExampleCli("protx", "register_submit \"tx\" \"sig\"")
},
}.ToString());
}.Check(request);
}
// handles register, register_prepare and register_fund in one method
static UniValue protx_register(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
return NullUniValue;
bool isExternalRegister = request.params[0].get_str() == "register";
bool isFundRegister = request.params[0].get_str() == "register_fund";
bool isPrepareRegister = request.params[0].get_str() == "register_prepare";
if (isFundRegister && (request.fHelp || (request.params.size() < 8 || request.params.size() > 10))) {
protx_register_fund_help(pwallet);
protx_register_fund_help(request);
} else if (isExternalRegister && (request.fHelp || (request.params.size() < 9 || request.params.size() > 11))) {
protx_register_help(pwallet);
protx_register_help(request);
} else if (isPrepareRegister && (request.fHelp || (request.params.size() != 9 && request.params.size() != 10))) {
protx_register_prepare_help();
protx_register_prepare_help(request);
}
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
if (isExternalRegister || isFundRegister) {
EnsureWalletIsUnlocked(pwallet);
}
@ -554,7 +549,7 @@ static UniValue protx_register(const JSONRPCRequest& request)
break;
}
}
assert(collateralIndex != (uint32_t) -1);
CHECK_NONFATAL(collateralIndex != (uint32_t) -1);
ptx.collateralOutpoint.n = collateralIndex;
SetTxPayload(tx, ptx);
@ -598,15 +593,14 @@ static UniValue protx_register(const JSONRPCRequest& request)
static UniValue protx_register_submit(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
return NullUniValue;
if (request.fHelp || request.params.size() != 3) {
protx_register_submit_help(pwallet);
protx_register_submit_help(request);
}
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
EnsureWalletIsUnlocked(pwallet);
CMutableTransaction tx;
@ -630,14 +624,13 @@ static UniValue protx_register_submit(const JSONRPCRequest& request)
return SignAndSendSpecialTx(tx);
}
static void protx_update_service_help(CWallet* const pwallet)
static void protx_update_service_help(const JSONRPCRequest& request)
{
throw std::runtime_error(
RPCHelpMan{"protx update_service",
"\nCreates and sends a ProUpServTx to the network. This will update the IP address\n"
"of a masternode.\n"
"If this is done for a masternode that got PoSe-banned, the ProUpServTx will also revive this masternode.\n"
+ HelpRequiringPassphrase(pwallet) + "\n",
+ HelpRequiringPassphrase() + "\n",
{
GetRpcArg("proTxHash"),
GetRpcArg("ipAndPort"),
@ -651,18 +644,17 @@ static void protx_update_service_help(CWallet* const pwallet)
RPCExamples{
HelpExampleCli("protx", "update_service \"0123456701234567012345670123456701234567012345670123456701234567\" \"1.2.3.4:1234\" 5a2e15982e62f1e0b7cf9783c64cf7e3af3f90a52d6c40f6f95d624c0b1621cd")
},
}.ToString());
}.Check(request);
}
static UniValue protx_update_service(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
return NullUniValue;
if (request.fHelp || (request.params.size() < 4 || request.params.size() > 6))
protx_update_service_help(pwallet);
protx_update_service_help(request);
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
EnsureWalletIsUnlocked(pwallet);
@ -729,14 +721,13 @@ static UniValue protx_update_service(const JSONRPCRequest& request)
return SignAndSendSpecialTx(tx);
}
static void protx_update_registrar_help(CWallet* const pwallet)
static void protx_update_registrar_help(const JSONRPCRequest& request)
{
throw std::runtime_error(
RPCHelpMan{"protx update_registrar",
"\nCreates and sends a ProUpRegTx to the network. This will update the operator key, voting key and payout\n"
"address of the masternode specified by \"proTxHash\".\n"
"The owner key of the masternode must be known to your wallet.\n"
+ HelpRequiringPassphrase(pwallet) + "\n",
+ HelpRequiringPassphrase() + "\n",
{
GetRpcArg("proTxHash"),
GetRpcArg("operatorPubKey_update"),
@ -750,20 +741,19 @@ static void protx_update_registrar_help(CWallet* const pwallet)
RPCExamples{
HelpExampleCli("protx", "update_registrar \"0123456701234567012345670123456701234567012345670123456701234567\" \"982eb34b7c7f614f29e5c665bc3605f1beeef85e3395ca12d3be49d2868ecfea5566f11cedfad30c51b2403f2ad95b67\" \"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwG\"")
},
}.ToString());
}.Check(request);
}
static UniValue protx_update_registrar(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
return NullUniValue;
if (request.fHelp || (request.params.size() != 5 && request.params.size() != 6)) {
protx_update_registrar_help(pwallet);
protx_update_registrar_help(request);
}
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
EnsureWalletIsUnlocked(pwallet);
CProUpRegTx ptx;
@ -821,15 +811,14 @@ static UniValue protx_update_registrar(const JSONRPCRequest& request)
return SignAndSendSpecialTx(tx);
}
static void protx_revoke_help(CWallet* const pwallet)
static void protx_revoke_help(const JSONRPCRequest& request)
{
throw std::runtime_error(
RPCHelpMan{"protx revoke",
"\nCreates and sends a ProUpRevTx to the network. This will revoke the operator key of the masternode and\n"
"put it into the PoSe-banned state. It will also set the service field of the masternode\n"
"to zero. Use this in case your operator key got compromised or you want to stop providing your service\n"
"to the masternode owner.\n"
+ HelpRequiringPassphrase(pwallet) + "\n",
+ HelpRequiringPassphrase() + "\n",
{
GetRpcArg("proTxHash"),
GetRpcArg("operatorKey"),
@ -842,20 +831,19 @@ static void protx_revoke_help(CWallet* const pwallet)
RPCExamples{
HelpExampleCli("protx", "revoke \"0123456701234567012345670123456701234567012345670123456701234567\" \"072f36a77261cdd5d64c32d97bac417540eddca1d5612f416feb07ff75a8e240\"")
},
}.ToString());
}.Check(request);
}
static UniValue protx_revoke(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
return NullUniValue;
if (request.fHelp || (request.params.size() < 3 || request.params.size() > 5)) {
protx_revoke_help(pwallet);
protx_revoke_help(request);
}
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
EnsureWalletIsUnlocked(pwallet);
CProUpRevTx ptx;
@ -911,9 +899,8 @@ static UniValue protx_revoke(const JSONRPCRequest& request)
}
#endif//ENABLE_WALLET
static void protx_list_help()
static void protx_list_help(const JSONRPCRequest& request)
{
throw std::runtime_error(
RPCHelpMan{"protx list",
"\nLists all ProTxs in your wallet or on-chain, depending on the given type.\n",
{
@ -932,7 +919,7 @@ static void protx_list_help()
},
RPCResults{},
RPCExamples{""},
}.ToString());
}.Check(request);
}
#ifdef ENABLE_WALLET
@ -1003,14 +990,19 @@ static UniValue BuildDMNListEntry(CWallet* pwallet, const CDeterministicMN& dmn,
static UniValue protx_list(const JSONRPCRequest& request)
{
if (request.fHelp) {
protx_list_help();
protx_list_help(request);
}
CWallet* pwallet;
#ifdef ENABLE_WALLET
try {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet* const pwallet = wallet.get();
pwallet = wallet.get();
} catch (...) {
pwallet = nullptr;
}
#else
CWallet* const pwallet = nullptr;
pwallet = nullptr;
#endif
std::string type = "registered";
@ -1032,7 +1024,7 @@ static UniValue protx_list(const JSONRPCRequest& request)
LOCK2(cs_main, pwallet->cs_wallet);
if (request.params.size() > 4) {
protx_list_help();
protx_list_help(request);
}
bool detailed = !request.params[2].isNull() ? ParseBoolV(request.params[2], "detailed") : false;
@ -1062,7 +1054,7 @@ static UniValue protx_list(const JSONRPCRequest& request)
#endif
} else if (type == "valid" || type == "registered") {
if (request.params.size() > 4) {
protx_list_help();
protx_list_help(request);
}
LOCK(cs_main);
@ -1086,9 +1078,8 @@ static UniValue protx_list(const JSONRPCRequest& request)
return ret;
}
static void protx_info_help()
static void protx_info_help(const JSONRPCRequest& request)
{
throw std::runtime_error(
RPCHelpMan{"protx info",
"\nReturns detailed information about a deterministic masternode.\n",
{
@ -1101,20 +1092,25 @@ static void protx_info_help()
RPCExamples{
HelpExampleCli("protx", "info \"0123456701234567012345670123456701234567012345670123456701234567\"")
},
}.ToString());
}.Check(request);
}
static UniValue protx_info(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 2) {
protx_info_help();
protx_info_help(request);
}
CWallet* pwallet;
#ifdef ENABLE_WALLET
try {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet* const pwallet = wallet.get();
pwallet = wallet.get();
} catch (...) {
pwallet = nullptr;
}
#else
CWallet* const pwallet = nullptr;
pwallet = nullptr;
#endif
if (g_txindex) {
@ -1130,9 +1126,8 @@ static UniValue protx_info(const JSONRPCRequest& request)
return BuildDMNListEntry(pwallet, *dmn, true);
}
static void protx_diff_help()
static void protx_diff_help(const JSONRPCRequest& request)
{
throw std::runtime_error(
RPCHelpMan{"protx diff",
"\nCalculates a diff between two deterministic masternode lists. The result also contains proof data.\n",
{
@ -1141,7 +1136,7 @@ static void protx_diff_help()
},
RPCResults{},
RPCExamples{""},
}.ToString());
}.Check(request);
}
static uint256 ParseBlock(const UniValue& v, std::string strName) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
@ -1161,7 +1156,7 @@ static uint256 ParseBlock(const UniValue& v, std::string strName) EXCLUSIVE_LOCK
static UniValue protx_diff(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 3) {
protx_diff_help();
protx_diff_help(request);
}
LOCK(cs_main);
@ -1181,7 +1176,6 @@ static UniValue protx_diff(const JSONRPCRequest& request)
[[ noreturn ]] static void protx_help()
{
throw std::runtime_error(
RPCHelpMan{"protx",
"Set of commands to execute ProTx related actions.\n"
"To get help on individual commands, use \"help protx command\".\n"
@ -1205,7 +1199,7 @@ static UniValue protx_diff(const JSONRPCRequest& request)
},
RPCResults{},
RPCExamples{""},
}.ToString());
}.Throw();
}
static UniValue protx(const JSONRPCRequest& request)
@ -1243,9 +1237,8 @@ static UniValue protx(const JSONRPCRequest& request)
}
}
static void bls_generate_help()
static void bls_generate_help(const JSONRPCRequest& request)
{
throw std::runtime_error(
RPCHelpMan{"bls generate",
"\nReturns a BLS secret/public key pair.\n",
{},
@ -1258,13 +1251,13 @@ static void bls_generate_help()
RPCExamples{
HelpExampleCli("bls generate", "")
},
}.ToString());
}.Check(request);
}
static UniValue bls_generate(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1) {
bls_generate_help();
bls_generate_help(request);
}
CBLSSecretKey sk;
@ -1276,9 +1269,8 @@ static UniValue bls_generate(const JSONRPCRequest& request)
return ret;
}
static void bls_fromsecret_help()
static void bls_fromsecret_help(const JSONRPCRequest& request)
{
throw std::runtime_error(
RPCHelpMan{"bls fromsecret",
"\nParses a BLS secret key and returns the secret/public key pair.\n",
{
@ -1293,13 +1285,13 @@ static void bls_fromsecret_help()
RPCExamples{
HelpExampleCli("bls fromsecret", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")
},
}.ToString());
}.Check(request);
}
static UniValue bls_fromsecret(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 2) {
bls_fromsecret_help();
bls_fromsecret_help(request);
}
CBLSSecretKey sk;
@ -1315,7 +1307,6 @@ static UniValue bls_fromsecret(const JSONRPCRequest& request)
[[ noreturn ]] static void bls_help()
{
throw std::runtime_error(
RPCHelpMan{"bls",
"Set of commands to execute BLS related actions.\n"
"To get help on individual commands, use \"help bls command\".\n"
@ -1327,7 +1318,7 @@ static UniValue bls_fromsecret(const JSONRPCRequest& request)
},
RPCResults{},
RPCExamples{""},
}.ToString());
}.Throw();
}
static UniValue _bls(const JSONRPCRequest& request)

View File

@ -23,9 +23,8 @@ namespace llmq {
extern const std::string CLSIG_REQUESTID_PREFIX;
}
static void quorum_list_help()
static void quorum_list_help(const JSONRPCRequest& request)
{
throw std::runtime_error(
RPCHelpMan{"quorum list",
"List of on-chain quorums\n",
{
@ -44,13 +43,13 @@ static void quorum_list_help()
+ HelpExampleCli("quorum", "list 10")
+ HelpExampleRpc("quorum", "list, 10")
},
}.ToString());
}.Check(request);
}
static UniValue quorum_list(const JSONRPCRequest& request)
{
if (request.fHelp || (request.params.size() != 1 && request.params.size() != 2))
quorum_list_help();
quorum_list_help(request);
int count = -1;
if (!request.params[1].isNull()) {
@ -80,9 +79,8 @@ static UniValue quorum_list(const JSONRPCRequest& request)
return ret;
}
static void quorum_info_help()
static void quorum_info_help(const JSONRPCRequest& request)
{
throw std::runtime_error(
RPCHelpMan{"quorum info",
"Return information about a quorum\n",
{
@ -92,7 +90,7 @@ static void quorum_info_help()
},
RPCResults{},
RPCExamples{""},
}.ToString());
}.Check(request);
}
static UniValue BuildQuorumInfo(const llmq::CQuorumCPtr& quorum, bool includeMembers, bool includeSkShare)
@ -134,7 +132,7 @@ static UniValue BuildQuorumInfo(const llmq::CQuorumCPtr& quorum, bool includeMem
static UniValue quorum_info(const JSONRPCRequest& request)
{
if (request.fHelp || (request.params.size() != 3 && request.params.size() != 4))
quorum_info_help();
quorum_info_help(request);
Consensus::LLMQType llmqType = (Consensus::LLMQType)ParseInt32V(request.params[1], "llmqType");
if (!Params().HasLLMQ(llmqType)) {
@ -155,9 +153,8 @@ static UniValue quorum_info(const JSONRPCRequest& request)
return BuildQuorumInfo(quorum, true, includeSkShare);
}
static void quorum_dkgstatus_help()
static void quorum_dkgstatus_help(const JSONRPCRequest& request)
{
throw std::runtime_error(
RPCHelpMan{"quorum dkgstatus",
"Return the status of the current DKG process.\n"
"Works only when SPORK_17_QUORUM_DKG_ENABLED spork is ON.\n",
@ -168,13 +165,13 @@ static void quorum_dkgstatus_help()
},
RPCResults{},
RPCExamples{""},
}.ToString());
}.Check(request);
}
static UniValue quorum_dkgstatus(const JSONRPCRequest& request)
{
if (request.fHelp || (request.params.size() < 1 || request.params.size() > 2)) {
quorum_dkgstatus_help();
quorum_dkgstatus_help(request);
}
int detailLevel = 0;
@ -241,9 +238,8 @@ static UniValue quorum_dkgstatus(const JSONRPCRequest& request)
return ret;
}
static void quorum_memberof_help()
static void quorum_memberof_help(const JSONRPCRequest& request)
{
throw std::runtime_error(
RPCHelpMan{"quorum memberof",
"Checks which quorums the given masternode is a member of.\n",
{
@ -254,13 +250,13 @@ static void quorum_memberof_help()
},
RPCResults{},
RPCExamples{""},
}.ToString());
}.Check(request);
}
static UniValue quorum_memberof(const JSONRPCRequest& request)
{
if (request.fHelp || (request.params.size() < 2 || request.params.size() > 3)) {
quorum_memberof_help();
quorum_memberof_help(request);
}
uint256 protxHash = ParseHashV(request.params[1], "proTxHash");
@ -302,9 +298,8 @@ static UniValue quorum_memberof(const JSONRPCRequest& request)
return result;
}
static void quorum_sign_help()
static void quorum_sign_help(const JSONRPCRequest& request)
{
throw std::runtime_error(
RPCHelpMan{"quorum sign",
"Threshold-sign a message\n",
{
@ -318,12 +313,11 @@ static void quorum_sign_help()
"\nReturns an object containing the signature share if this is false.\n"
},
RPCExamples{""},
}.ToString());
}.Check(request);
}
static void quorum_verify_help()
static void quorum_verify_help(const JSONRPCRequest& request)
{
throw std::runtime_error(
RPCHelpMan{"quorum verify",
"Test if a quorum signature is valid for a request id and a message hash\n",
{
@ -340,12 +334,11 @@ static void quorum_verify_help()
},
RPCResults{},
RPCExamples{""},
}.ToString());
}.Check(request);
}
static void quorum_hasrecsig_help()
static void quorum_hasrecsig_help(const JSONRPCRequest& request)
{
throw std::runtime_error(
RPCHelpMan{"quorum hasrecsig",
"Test if a valid recovered signature is present\n",
{
@ -355,12 +348,11 @@ static void quorum_hasrecsig_help()
},
RPCResults{},
RPCExamples{""},
}.ToString());
}.Check(request);
}
static void quorum_getrecsig_help()
static void quorum_getrecsig_help(const JSONRPCRequest& request)
{
throw std::runtime_error(
RPCHelpMan{"quorum getrecsig",
"Get a recovered signature\n",
{
@ -370,12 +362,11 @@ static void quorum_getrecsig_help()
},
RPCResults{},
RPCExamples{""},
}.ToString());
}.Check(request);
}
static void quorum_isconflicting_help()
static void quorum_isconflicting_help(const JSONRPCRequest& request)
{
throw std::runtime_error(
RPCHelpMan{"quorum isconflicting",
"Test if a conflict exists\n",
{
@ -385,7 +376,7 @@ static void quorum_isconflicting_help()
},
RPCResults{},
RPCExamples{""},
}.ToString());
}.Check(request);
}
static UniValue quorum_sigs_cmd(const JSONRPCRequest& request)
@ -394,18 +385,18 @@ static UniValue quorum_sigs_cmd(const JSONRPCRequest& request)
if (request.fHelp || (request.params.size() != 4)) {
if (cmd == "sign") {
if ((request.params.size() < 4) || (request.params.size() > 6)) {
quorum_sign_help();
quorum_sign_help(request);
}
} else if (cmd == "verify") {
if (request.params.size() < 5 || request.params.size() > 7) {
quorum_verify_help();
quorum_verify_help(request);
}
} else if (cmd == "hasrecsig") {
quorum_hasrecsig_help();
quorum_hasrecsig_help(request);
} else if (cmd == "getrecsig") {
quorum_getrecsig_help();
quorum_getrecsig_help(request);
} else if (cmd == "isconflicting") {
quorum_isconflicting_help();
quorum_isconflicting_help(request);
} else {
// shouldn't happen as it's already handled by the caller
throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid cmd");
@ -507,9 +498,8 @@ static UniValue quorum_sigs_cmd(const JSONRPCRequest& request)
}
}
static void quorum_selectquorum_help()
static void quorum_selectquorum_help(const JSONRPCRequest& request)
{
throw std::runtime_error(
RPCHelpMan{"quorum selectquorum",
"Returns the quorum that would/should sign a request\n",
{
@ -518,13 +508,13 @@ static void quorum_selectquorum_help()
},
RPCResults{},
RPCExamples{""},
}.ToString());
}.Check(request);
}
static UniValue quorum_selectquorum(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 3) {
quorum_selectquorum_help();
quorum_selectquorum_help(request);
}
Consensus::LLMQType llmqType = (Consensus::LLMQType)ParseInt32V(request.params[1], "llmqType");
@ -552,9 +542,8 @@ static UniValue quorum_selectquorum(const JSONRPCRequest& request)
return ret;
}
static void quorum_dkgsimerror_help()
static void quorum_dkgsimerror_help(const JSONRPCRequest& request)
{
throw std::runtime_error(
RPCHelpMan{"quorum dkgsimerror",
"This enables simulation of errors and malicious behaviour in the DKG. Do NOT use this on mainnet\n"
"as you will get yourself very likely PoSe banned for this.\n",
@ -564,13 +553,13 @@ static void quorum_dkgsimerror_help()
},
RPCResults{},
RPCExamples{""},
}.ToString());
}.Check(request);
}
static UniValue quorum_dkgsimerror(const JSONRPCRequest& request)
{
if (request.fHelp || (request.params.size() != 3)) {
quorum_dkgsimerror_help();
quorum_dkgsimerror_help(request);
}
std::string type = request.params[1].get_str();
@ -585,9 +574,8 @@ static UniValue quorum_dkgsimerror(const JSONRPCRequest& request)
return UniValue();
}
static void quorum_getdata_help()
static void quorum_getdata_help(const JSONRPCRequest& request)
{
throw std::runtime_error(
RPCHelpMan{"quorum getdata",
"Send a QGETDATA message to the specified peer.\n",
{
@ -603,13 +591,13 @@ static void quorum_getdata_help()
},
RPCResults{},
RPCExamples{""},
}.ToString());
}.Check(request);
}
static UniValue quorum_getdata(const JSONRPCRequest& request)
{
if (request.fHelp || (request.params.size() < 5 || request.params.size() > 6)) {
quorum_getdata_help();
quorum_getdata_help(request);
}
NodeId nodeId = ParseInt64V(request.params[1], "nodeId");
@ -639,7 +627,6 @@ static UniValue quorum_getdata(const JSONRPCRequest& request)
[[ noreturn ]] static void quorum_help()
{
throw std::runtime_error(
RPCHelpMan{"quorum",
"Set of commands for quorums/LLMQs.\n"
"To get help on individual commands, use \"help quorum command\".\n"
@ -661,7 +648,7 @@ static UniValue quorum_getdata(const JSONRPCRequest& request)
},
RPCResults{},
RPCExamples{""},
}.ToString());
}.Throw();
}
static UniValue _quorum(const JSONRPCRequest& request)
@ -696,9 +683,8 @@ static UniValue _quorum(const JSONRPCRequest& request)
}
}
static void verifychainlock_help()
static void verifychainlock_help(const JSONRPCRequest& request)
{
throw std::runtime_error(
RPCHelpMan{"verifychainlock",
"Test if a quorum signature is valid for a ChainLock.\n",
{
@ -708,13 +694,13 @@ static void verifychainlock_help()
},
RPCResults{},
RPCExamples{""},
}.ToString());
}.Check(request);
}
static UniValue verifychainlock(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 2 || request.params.size() > 3) {
verifychainlock_help();
verifychainlock_help(request);
}
const uint256 nBlockHash = ParseHashV(request.params[0], "blockHash");
@ -740,9 +726,8 @@ static UniValue verifychainlock(const JSONRPCRequest& request)
return llmq::CSigningManager::VerifyRecoveredSig(llmqType, nBlockHeight, nRequestId, nBlockHash, chainLockSig);
}
static void verifyislock_help()
static void verifyislock_help(const JSONRPCRequest& request)
{
throw std::runtime_error(
RPCHelpMan{"verifyislock",
"Test if a quorum signature is valid for an InstantSend Lock\n",
{
@ -753,13 +738,13 @@ static void verifyislock_help()
},
RPCResults{},
RPCExamples{""},
}.ToString());
}.Check(request);
}
static UniValue verifyislock(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 3 || request.params.size() > 4) {
verifyislock_help();
verifyislock_help(request);
}
uint256 id = ParseHashV(request.params[0], "id");

View File

@ -161,7 +161,7 @@ UniValue help(const JSONRPCRequest& jsonRequest)
{"subcommand", RPCArg::Type::STR, /* default */ "all subcommands", "The subcommand to get help on. Please note that not all subcommands support this at the moment"},
},
RPCResult{
"\"text\" (string) The help text\n"
RPCResult::Type::STR, "", "The help text"
},
RPCExamples{""},
}.ToString()
@ -208,7 +208,7 @@ static UniValue uptime(const JSONRPCRequest& jsonRequest)
"\nReturns the total uptime of the server.\n",
{},
RPCResult{
"ttt (numeric) The number of seconds that the server has been running\n"
RPCResult::Type::NUM, "", "The number of seconds that the server has been running"
},
RPCExamples{
HelpExampleCli("uptime", "")
@ -341,41 +341,6 @@ bool RPCIsInWarmup(std::string *outStatus)
return fRPCInWarmup;
}
void JSONRPCRequest::parse(const UniValue& valRequest)
{
// Parse request
if (!valRequest.isObject())
throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object");
const UniValue& request = valRequest.get_obj();
// Parse id now so errors from here on will have the id
id = find_value(request, "id");
// Parse method
UniValue valMethod = find_value(request, "method");
if (valMethod.isNull())
throw JSONRPCError(RPC_INVALID_REQUEST, "Missing method");
if (!valMethod.isStr())
throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string");
strMethod = valMethod.get_str();
if (strMethod != "getblocktemplate") {
if (fLogIPs)
LogPrint(BCLog::RPC, "ThreadRPCServer method=%s user=%s peeraddr=%s\n", SanitizeString(strMethod),
this->authUser, this->peerAddr);
else
LogPrint(BCLog::RPC, "ThreadRPCServer method=%s user=%s\n", SanitizeString(strMethod), this->authUser);
}
// Parse params
UniValue valParams = find_value(request, "params");
if (valParams.isArray() || valParams.isObject())
params = valParams;
else if (valParams.isNull())
params = UniValue(UniValue::VARR);
else
throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array or object");
}
bool IsDeprecatedRPCEnabled(const std::string& method)
{
const std::vector<std::string> enabled_methods = gArgs.GetArgs("-deprecatedrpc");

View File

@ -7,11 +7,13 @@
#define BITCOIN_RPC_SERVER_H
#include <amount.h>
#include <rpc/protocol.h>
#include <rpc/request.h>
#include <rpc/util.h>
#include <map>
#include <stdint.h>
#include <string>
#include <functional>
#include <univalue.h>
@ -23,21 +25,6 @@ namespace RPCServer
void OnStopped(std::function<void ()> slot);
}
class JSONRPCRequest
{
public:
UniValue id;
std::string strMethod;
UniValue params;
bool fHelp;
std::string URI;
std::string authUser;
std::string peerAddr;
JSONRPCRequest() : id(NullUniValue), params(NullUniValue), fHelp(false) {}
void parse(const UniValue& valRequest);
};
/** Query whether RPC is running */
bool IsRPCRunning();
@ -94,6 +81,7 @@ void RPCUnsetTimerInterface(RPCTimerInterface *iface);
void RPCRunLater(const std::string& name, std::function<void()> func, int64_t nSeconds);
typedef UniValue(*rpcfn_type)(const JSONRPCRequest& jsonRequest);
typedef RPCHelpMan (*RpcMethodFnType)();
class CRPCCommand
{
@ -110,6 +98,19 @@ public:
{
}
//! Simplified constructor taking plain RpcMethodFnType function pointer.
CRPCCommand(std::string category, std::string name_in, RpcMethodFnType fn, std::vector<std::string> args_in)
: CRPCCommand(
category,
fn().m_name,
[fn](const JSONRPCRequest& request, UniValue& result, bool) { result = fn().HandleRequest(request); return true; },
fn().GetArgNames(),
intptr_t(fn))
{
CHECK_NONFATAL(fn().m_name == name_in);
CHECK_NONFATAL(fn().GetArgNames() == args_in);
}
//! Simplified constructor taking plain rpcfn_type function pointer.
CRPCCommand(const char* category, const char* name, rpcfn_type fn, std::initializer_list<const char*> args)
: CRPCCommand(category, name,
@ -126,7 +127,7 @@ public:
};
/**
* Dash RPC command dispatcher.
* RPC command dispatcher.
*/
class CRPCTable
{

View File

@ -13,6 +13,9 @@
#include <tuple>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
InitInterfaces* g_rpc_interfaces = nullptr;
void RPCTypeCheck(const UniValue& params,
@ -263,13 +266,21 @@ unsigned int ParseConfirmTarget(const UniValue& value, unsigned int max_target)
return (unsigned int)target;
}
/**
* A pair of strings that can be aligned (through padding) with other Sections
* later on
*/
struct Section {
Section(const std::string& left, const std::string& right)
: m_left{left}, m_right{right} {}
const std::string m_left;
std::string m_left;
const std::string m_right;
};
/**
* Keeps track of RPCArgs by transforming them into sections for the purpose
* of serializing everything to a single string
*/
struct Sections {
std::vector<Section> m_sections;
size_t m_max_pad{0};
@ -280,16 +291,15 @@ struct Sections {
m_sections.push_back(s);
}
enum class OuterType {
ARR,
OBJ,
NAMED_ARG, // Only set on first recursion
};
void Push(const RPCArg& arg, const size_t current_indent = 5, const OuterType outer_type = OuterType::NAMED_ARG)
/**
* Recursive helper to translate an RPCArg into sections
*/
void Push(const RPCArg& arg, const size_t current_indent = 5, const OuterType outer_type = OuterType::NONE)
{
const auto indent = std::string(current_indent, ' ');
const auto indent_next = std::string(current_indent + 2, ' ');
const bool push_name{outer_type == OuterType::OBJ}; // Dictionary keys must have a name
switch (arg.m_type) {
case RPCArg::Type::STR_HEX:
case RPCArg::Type::STR:
@ -297,12 +307,12 @@ struct Sections {
case RPCArg::Type::AMOUNT:
case RPCArg::Type::RANGE:
case RPCArg::Type::BOOL: {
if (outer_type == OuterType::NAMED_ARG) return; // Nothing more to do for non-recursive types on first recursion
if (outer_type == OuterType::NONE) return; // Nothing more to do for non-recursive types on first recursion
auto left = indent;
if (arg.m_type_str.size() != 0 && outer_type == OuterType::OBJ) {
left += "\"" + arg.m_name + "\": " + arg.m_type_str.at(0);
if (arg.m_type_str.size() != 0 && push_name) {
left += "\"" + arg.GetName() + "\": " + arg.m_type_str.at(0);
} else {
left += outer_type == OuterType::OBJ ? arg.ToStringObj(/* oneline */ false) : arg.ToString(/* oneline */ false);
left += push_name ? arg.ToStringObj(/* oneline */ false) : arg.ToString(/* oneline */ false);
}
left += ",";
PushSection({left, arg.ToDescriptionString()});
@ -310,40 +320,44 @@ struct Sections {
}
case RPCArg::Type::OBJ:
case RPCArg::Type::OBJ_USER_KEYS: {
const auto right = outer_type == OuterType::NAMED_ARG ? "" : arg.ToDescriptionString();
PushSection({indent + "{", right});
const auto right = outer_type == OuterType::NONE ? "" : arg.ToDescriptionString();
PushSection({indent + (push_name ? "\"" + arg.GetName() + "\": " : "") + "{", right});
for (const auto& arg_inner : arg.m_inner) {
Push(arg_inner, current_indent + 2, OuterType::OBJ);
}
if (arg.m_type != RPCArg::Type::OBJ) {
PushSection({indent_next + "...", ""});
}
PushSection({indent + "}" + (outer_type != OuterType::NAMED_ARG ? "," : ""), ""});
PushSection({indent + "}" + (outer_type != OuterType::NONE ? "," : ""), ""});
break;
}
case RPCArg::Type::ARR: {
auto left = indent;
left += outer_type == OuterType::OBJ ? "\"" + arg.m_name + "\": " : "";
left += push_name ? "\"" + arg.GetName() + "\": " : "";
left += "[";
const auto right = outer_type == OuterType::NAMED_ARG ? "" : arg.ToDescriptionString();
const auto right = outer_type == OuterType::NONE ? "" : arg.ToDescriptionString();
PushSection({left, right});
for (const auto& arg_inner : arg.m_inner) {
Push(arg_inner, current_indent + 2, OuterType::ARR);
}
PushSection({indent_next + "...", ""});
PushSection({indent + "]" + (outer_type != OuterType::NAMED_ARG ? "," : ""), ""});
PushSection({indent + "]" + (outer_type != OuterType::NONE ? "," : ""), ""});
break;
}
// no default case, so the compiler can warn about missing cases
}
} // no default case, so the compiler can warn about missing cases
}
/**
* Concatenate all sections with proper padding
*/
std::string ToString() const
{
std::string ret;
const size_t pad = m_max_pad + 4;
for (const auto& s : m_sections) {
// The left part of a section is assumed to be a single line, usually it is the name of the JSON struct or a
// brace like {, }, [, or ]
CHECK_NONFATAL(s.m_left.find('\n') == std::string::npos);
if (s.m_right.empty()) {
ret += s.m_left;
ret += "\n";
@ -378,7 +392,11 @@ struct Sections {
};
RPCHelpMan::RPCHelpMan(std::string name, std::string description, std::vector<RPCArg> args, RPCResults results, RPCExamples examples)
: RPCHelpMan{std::move(name), std::move(description), std::move(args), std::move(results), std::move(examples), nullptr} {}
RPCHelpMan::RPCHelpMan(std::string name, std::string description, std::vector<RPCArg> args, RPCResults results, RPCExamples examples, RPCMethodImpl fun)
: m_name{std::move(name)},
m_fun{std::move(fun)},
m_description{std::move(description)},
m_args{std::move(args)},
m_results{std::move(results)},
@ -386,8 +404,12 @@ RPCHelpMan::RPCHelpMan(std::string name, std::string description, std::vector<RP
{
std::set<std::string> named_args;
for (const auto& arg : m_args) {
std::vector<std::string> names;
boost::split(names, arg.m_names, boost::is_any_of("|"));
// Should have unique named arguments
assert(named_args.insert(arg.m_name).second);
for (const std::string& name : names) {
CHECK_NONFATAL(named_args.insert(name).second);
}
}
}
@ -400,7 +422,13 @@ std::string RPCResults::ToDescriptionString() const
} else {
result += "\nResult (" + r.m_cond + "):\n";
}
if (r.m_legacy) {
result += r.m_result;
} else {
Sections sections;
r.ToSections(sections);
result += sections.ToString();
}
}
return result;
}
@ -421,6 +449,16 @@ bool RPCHelpMan::IsValidNumArgs(size_t num_args) const
}
return num_required_args <= num_args && num_args <= m_args.size();
}
std::vector<std::string> RPCHelpMan::GetArgNames() const
{
std::vector<std::string> ret;
for (const auto& arg : m_args) {
ret.emplace_back(arg.m_names);
}
return ret;
}
std::string RPCHelpMan::ToString() const
{
std::string ret;
@ -429,6 +467,7 @@ std::string RPCHelpMan::ToString() const
ret += m_name;
bool was_optional{false};
for (const auto& arg : m_args) {
if (arg.m_hidden) continue;
const bool optional = arg.IsOptional();
ret += " ";
if (optional) {
@ -450,11 +489,12 @@ std::string RPCHelpMan::ToString() const
Sections sections;
for (size_t i{0}; i < m_args.size(); ++i) {
const auto& arg = m_args.at(i);
if (arg.m_hidden) continue;
if (i == 0) ret += "\nArguments:\n";
// Push named argument name and description
sections.m_sections.emplace_back(std::to_string(i + 1) + ". " + arg.m_name, arg.ToDescriptionString());
sections.m_sections.emplace_back(std::to_string(i + 1) + ". " + arg.GetFirstName(), arg.ToDescriptionString());
sections.m_max_pad = std::max(sections.m_max_pad, sections.m_sections.back().m_left.size());
// Recursively push nested args
@ -471,6 +511,17 @@ std::string RPCHelpMan::ToString() const
return ret;
}
std::string RPCArg::GetFirstName() const
{
return m_names.substr(0, m_names.find("|"));
}
std::string RPCArg::GetName() const
{
CHECK_NONFATAL(std::string::npos == m_names.find("|"));
return m_names;
}
bool RPCArg::IsOptional() const
{
if (m_fallback.which() == 1) {
@ -518,9 +569,7 @@ std::string RPCArg::ToDescriptionString() const
ret += "json array";
break;
}
// no default case, so the compiler can warn about missing cases
}
} // no default case, so the compiler can warn about missing cases
}
if (m_fallback.which() == 1) {
ret += ", optional, default=" + boost::get<std::string>(m_fallback);
@ -538,20 +587,110 @@ std::string RPCArg::ToDescriptionString() const
ret += ", required";
break;
}
// no default case, so the compiler can warn about missing cases
}
} // no default case, so the compiler can warn about missing cases
}
ret += ")";
ret += m_description.empty() ? "" : " " + m_description;
return ret;
}
void RPCResult::ToSections(Sections& sections, const OuterType outer_type, const int current_indent) const
{
// Indentation
const std::string indent(current_indent, ' ');
const std::string indent_next(current_indent + 2, ' ');
// Elements in a JSON structure (dictionary or array) are separated by a comma
const std::string maybe_separator{outer_type != OuterType::NONE ? "," : ""};
// The key name if recursed into an dictionary
const std::string maybe_key{
outer_type == OuterType::OBJ ?
"\"" + this->m_key_name + "\" : " :
""};
// Format description with type
const auto Description = [&](const std::string& type) {
return "(" + type + (this->m_optional ? ", optional" : "") + ")" +
(this->m_description.empty() ? "" : " " + this->m_description);
};
switch (m_type) {
case Type::ELISION: {
// If the inner result is empty, use three dots for elision
sections.PushSection({indent_next + "...", m_description});
return;
}
case Type::NONE: {
sections.PushSection({indent + "None", Description("json null")});
return;
}
case Type::STR: {
sections.PushSection({indent + maybe_key + "\"str\"" + maybe_separator, Description("string")});
return;
}
case Type::STR_AMOUNT: {
sections.PushSection({indent + maybe_key + "n" + maybe_separator, Description("numeric")});
return;
}
case Type::STR_HEX: {
sections.PushSection({indent + maybe_key + "\"hex\"" + maybe_separator, Description("string")});
return;
}
case Type::NUM: {
sections.PushSection({indent + maybe_key + "n" + maybe_separator, Description("numeric")});
return;
}
case Type::NUM_TIME: {
sections.PushSection({indent + maybe_key + "xxx" + maybe_separator, Description("numeric")});
return;
}
case Type::BOOL: {
sections.PushSection({indent + maybe_key + "true|false" + maybe_separator, Description("boolean")});
return;
}
case Type::ARR_FIXED:
case Type::ARR: {
sections.PushSection({indent + maybe_key + "[", Description("json array")});
for (const auto& i : m_inner) {
i.ToSections(sections, OuterType::ARR, current_indent + 2);
}
if (m_type == Type::ARR) {
sections.PushSection({indent_next + "...", ""});
} else {
CHECK_NONFATAL(!m_inner.empty());
// Remove final comma, which would be invalid JSON
sections.m_sections.back().m_left.pop_back();
}
sections.PushSection({indent + "]" + maybe_separator, ""});
return;
}
case Type::OBJ_DYN:
case Type::OBJ: {
sections.PushSection({indent + maybe_key + "{", Description("json object")});
for (const auto& i : m_inner) {
i.ToSections(sections, OuterType::OBJ, current_indent + 2);
}
if (m_type == Type::OBJ_DYN) {
// If the dictionary keys are dynamic, use three dots for continuation
sections.PushSection({indent_next + "...", ""});
} else {
CHECK_NONFATAL(!m_inner.empty());
// Remove final comma, which would be invalid JSON
sections.m_sections.back().m_left.pop_back();
}
sections.PushSection({indent + "}" + maybe_separator, ""});
return;
}
} // no default case, so the compiler can warn about missing cases
CHECK_NONFATAL(false);
}
std::string RPCArg::ToStringObj(const bool oneline) const
{
std::string res;
res += "\"";
res += m_name;
res += GetFirstName();
if (oneline) {
res += "\":";
} else {
@ -579,11 +718,9 @@ std::string RPCArg::ToStringObj(const bool oneline) const
case Type::OBJ:
case Type::OBJ_USER_KEYS:
// Currently unused, so avoid writing dead code
assert(false);
// no default case, so the compiler can warn about missing cases
}
assert(false);
CHECK_NONFATAL(false);
} // no default case, so the compiler can warn about missing cases
CHECK_NONFATAL(false);
}
std::string RPCArg::ToString(const bool oneline) const
@ -593,13 +730,13 @@ std::string RPCArg::ToString(const bool oneline) const
switch (m_type) {
case Type::STR_HEX:
case Type::STR: {
return "\"" + m_name + "\"";
return "\"" + GetFirstName() + "\"";
}
case Type::NUM:
case Type::RANGE:
case Type::AMOUNT:
case Type::BOOL: {
return m_name;
return GetFirstName();
}
case Type::OBJ:
case Type::OBJ_USER_KEYS: {
@ -621,10 +758,8 @@ std::string RPCArg::ToString(const bool oneline) const
}
return "[" + res + "...]";
}
// no default case, so the compiler can warn about missing cases
}
assert(false);
} // no default case, so the compiler can warn about missing cases
CHECK_NONFATAL(false);
}
static std::pair<int64_t, int64_t> ParseRange(const UniValue& value)

View File

@ -6,11 +6,13 @@
#define BITCOIN_RPC_UTIL_H
#include <node/transaction.h>
#include <pubkey.h>
#include <protocol.h>
#include <pubkey.h>
#include <rpc/protocol.h>
#include <rpc/request.h>
#include <script/standard.h>
#include <univalue.h>
#include <util/check.h>
#include <util/strencodings.h>
#include <string>
@ -21,6 +23,7 @@
class CKeyStore;
class CPubKey;
class CScript;
struct Sections;
struct InitInterfaces;
//! Pointers to interfaces that need to be accessible from RPC methods. Due to
@ -90,6 +93,16 @@ UniValue GetServicesNames(ServiceFlags services);
//! Parse a JSON range specified as int64, or [int64, int64]
std::pair<int64_t, int64_t> ParseDescriptorRange(const UniValue& value);
/**
* Serializing JSON objects depends on the outer type. Only arrays and
* dictionaries can be nested in json. The top-level outer type is "NONE".
*/
enum class OuterType {
ARR,
OBJ,
NONE, // Only set on first recursion
};
struct RPCArg {
enum class Type {
OBJ,
@ -120,8 +133,9 @@ struct RPCArg {
OMITTED,
};
using Fallback = boost::variant<Optional, /* default value for optional args */ std::string>;
const std::string m_name; //!< The name of the arg (can be empty for inner args)
const std::string m_names; //!< The name of the arg (can be empty for inner args, can contain multiple aliases separated by | for named request arguments)
const Type m_type;
const bool m_hidden;
const std::vector<RPCArg> m_inner; //!< Only used for arrays or dicts
const Fallback m_fallback;
const std::string m_description;
@ -129,43 +143,52 @@ struct RPCArg {
const std::vector<std::string> m_type_str; //!< Should be empty unless it is supposed to override the auto-generated type strings. Vector length is either 0 or 2, m_type_str.at(0) will override the type of the value in a key-value pair, m_type_str.at(1) will override the type in the argument description.
RPCArg(
const std::string& name,
const Type& type,
const Fallback& fallback,
const std::string& description,
const std::string& oneline_description = "",
const std::vector<std::string>& type_str = {})
: m_name{name},
m_type{type},
m_fallback{fallback},
m_description{description},
m_oneline_description{oneline_description},
m_type_str{type_str}
const std::string name,
const Type type,
const Fallback fallback,
const std::string description,
const std::string oneline_description = "",
const std::vector<std::string> type_str = {},
const bool hidden = false)
: m_names{std::move(name)},
m_type{std::move(type)},
m_hidden{hidden},
m_fallback{std::move(fallback)},
m_description{std::move(description)},
m_oneline_description{std::move(oneline_description)},
m_type_str{std::move(type_str)}
{
assert(type != Type::ARR && type != Type::OBJ);
CHECK_NONFATAL(type != Type::ARR && type != Type::OBJ);
}
RPCArg(
const std::string& name,
const Type& type,
const Fallback& fallback,
const std::string& description,
const std::vector<RPCArg>& inner,
const std::string& oneline_description = "",
const std::vector<std::string>& type_str = {})
: m_name{name},
m_type{type},
m_inner{inner},
m_fallback{fallback},
m_description{description},
m_oneline_description{oneline_description},
m_type_str{type_str}
const std::string name,
const Type type,
const Fallback fallback,
const std::string description,
const std::vector<RPCArg> inner,
const std::string oneline_description = "",
const std::vector<std::string> type_str = {})
: m_names{std::move(name)},
m_type{std::move(type)},
m_hidden{false},
m_inner{std::move(inner)},
m_fallback{std::move(fallback)},
m_description{std::move(description)},
m_oneline_description{std::move(oneline_description)},
m_type_str{std::move(type_str)}
{
assert(type == Type::ARR || type == Type::OBJ);
CHECK_NONFATAL(type == Type::ARR || type == Type::OBJ);
}
bool IsOptional() const;
/** Return the first of all aliases */
std::string GetFirstName() const;
/** Return the name, throws when there are aliases */
std::string GetName() const;
/**
* Return the type string of the argument.
* Set oneline to allow it to be overridden by a custom oneline type string (m_oneline_description).
@ -184,21 +207,120 @@ struct RPCArg {
};
struct RPCResult {
enum class Type {
OBJ,
ARR,
STR,
NUM,
BOOL,
NONE,
STR_AMOUNT, //!< Special string to represent a floating point amount
STR_HEX, //!< Special string with only hex chars
OBJ_DYN, //!< Special dictionary with keys that are not literals
ARR_FIXED, //!< Special array that has a fixed number of entries
NUM_TIME, //!< Special numeric to denote unix epoch time
ELISION, //!< Special type to denote elision (...)
};
const Type m_type;
const std::string m_key_name; //!< Only used for dicts
const std::vector<RPCResult> m_inner; //!< Only used for arrays or dicts
const bool m_optional;
const std::string m_description;
const std::string m_cond;
const std::string m_result;
const bool m_legacy; //!< Used for legacy support
const std::string m_result; //!< Used for legacy support
explicit RPCResult(std::string result)
: m_cond{}, m_result{std::move(result)}
RPCResult(
const std::string cond,
const Type type,
const std::string m_key_name,
const bool optional,
const std::string description,
const std::vector<RPCResult> inner = {})
: m_type{std::move(type)},
m_key_name{std::move(m_key_name)},
m_inner{std::move(inner)},
m_optional{optional},
m_description{std::move(description)},
m_cond{std::move(cond)},
m_result{},
m_legacy{false}
{
assert(!m_result.empty());
CHECK_NONFATAL(!m_cond.empty());
const bool inner_needed{type == Type::ARR || type == Type::ARR_FIXED || type == Type::OBJ || type == Type::OBJ_DYN};
CHECK_NONFATAL(inner_needed != inner.empty());
}
// start legacy support logic
RPCResult(std::string cond, std::string result)
: m_cond{std::move(cond)}, m_result{std::move(result)}
: m_type{Type::NONE},
m_key_name{},
m_inner{},
m_optional{false},
m_description{},
m_cond{std::move(cond)},
m_result{std::move(result)},
m_legacy{true}
{
assert(!m_cond.empty());
assert(!m_result.empty());
CHECK_NONFATAL(!m_cond.empty());
CHECK_NONFATAL(!m_result.empty());
}
RPCResult(std::string result)
: m_type{Type::NONE},
m_key_name{},
m_inner{},
m_optional{false},
m_description{},
m_cond{},
m_result{std::move(result)},
m_legacy{true}
{
CHECK_NONFATAL(!m_result.empty());
}
// end legacy support logic
RPCResult(
const std::string cond,
const Type type,
const std::string m_key_name,
const std::string description,
const std::vector<RPCResult> inner = {})
: RPCResult{cond, type, m_key_name, false, description, inner} {}
RPCResult(
const Type type,
const std::string m_key_name,
const bool optional,
const std::string description,
const std::vector<RPCResult> inner = {})
: m_type{std::move(type)},
m_key_name{std::move(m_key_name)},
m_inner{std::move(inner)},
m_optional{optional},
m_description{std::move(description)},
m_cond{},
m_result{},
m_legacy{false}
{
const bool inner_needed{type == Type::ARR || type == Type::ARR_FIXED || type == Type::OBJ || type == Type::OBJ_DYN};
CHECK_NONFATAL(inner_needed != inner.empty());
}
RPCResult(
const Type type,
const std::string m_key_name,
const std::string description,
const std::vector<RPCResult> inner = {})
: RPCResult{type, m_key_name, false, description, inner} {}
/** Append the sections of the result. */
void ToSections(Sections& sections, OuterType outer_type = OuterType::NONE, const int current_indent = 0) const;
/** Return the type string of the result when it is in an object (dict). */
std::string ToStringObj() const;
/** Return the description string, including the result type. */
std::string ToDescriptionString() const;
};
struct RPCResults {
@ -239,13 +361,37 @@ class RPCHelpMan
{
public:
RPCHelpMan(std::string name, std::string description, std::vector<RPCArg> args, RPCResults results, RPCExamples examples);
using RPCMethodImpl = std::function<UniValue(const RPCHelpMan&, const JSONRPCRequest&)>;
RPCHelpMan(std::string name, std::string description, std::vector<RPCArg> args, RPCResults results, RPCExamples examples, RPCMethodImpl fun);
std::string ToString() const;
UniValue HandleRequest(const JSONRPCRequest& request)
{
Check(request);
return m_fun(*this, request);
}
/** If the supplied number of args is neither too small nor too high */
bool IsValidNumArgs(size_t num_args) const;
/**
* Check if the given request is valid according to this command or if
* the user is asking for help information, and throw help when appropriate.
*/
inline void Check(const JSONRPCRequest& request) const {
if (request.fHelp || !IsValidNumArgs(request.params.size())) {
throw std::runtime_error(ToString());
}
}
[[ noreturn ]] inline void Throw() const {
throw std::runtime_error(ToString());
}
std::vector<std::string> GetArgNames() const;
const std::string m_name;
private:
const std::string m_name;
const RPCMethodImpl m_fun;
const std::string m_description;
const std::vector<RPCArg> m_args;
const RPCResults m_results;

41
src/util/check.h Normal file
View File

@ -0,0 +1,41 @@
// Copyright (c) 2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_UTIL_CHECK_H
#define BITCOIN_UTIL_CHECK_H
#include <tinyformat.h>
#include <stdexcept>
class NonFatalCheckError : public std::runtime_error
{
using std::runtime_error::runtime_error;
};
/**
* Throw a NonFatalCheckError when the condition evaluates to false
*
* This should only be used
* - where the condition is assumed to be true, not for error handling or validating user input
* - where a failure to fulfill the condition is recoverable and does not abort the program
*
* For example in RPC code, where it is undersirable to crash the whole program, this can be generally used to replace
* asserts or recoverable logic errors. A NonFatalCheckError in RPC code is caught and passed as a string to the RPC
* caller, which can then report the issue to the developers.
*/
#define CHECK_NONFATAL(condition) \
do { \
if (!(condition)) { \
throw NonFatalCheckError( \
strprintf("%s:%d (%s)\n" \
"Internal bug detected: '%s'\n" \
"You may report this issue here: %s\n", \
__FILE__, __LINE__, __func__, \
(#condition), \
PACKAGE_BUGREPORT)); \
} \
} while (false)
#endif // BITCOIN_UTIL_CHECK_H

View File

@ -83,12 +83,6 @@ static void RescanWallet(CWallet& wallet, const WalletRescanReserver& reserver,
UniValue importprivkey(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
throw std::runtime_error(
RPCHelpMan{"importprivkey",
@ -116,6 +110,10 @@ UniValue importprivkey(const JSONRPCRequest& request)
},
}.ToString());
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import private keys to a wallet with private keys disabled");
}
@ -153,7 +151,7 @@ UniValue importprivkey(const JSONRPCRequest& request)
if (!key.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
CPubKey pubkey = key.GetPubKey();
assert(key.VerifyPubKey(pubkey));
CHECK_NONFATAL(key.VerifyPubKey(pubkey));
CKeyID vchAddress = pubkey.GetID();
{
pwallet->MarkDirty();
@ -184,12 +182,6 @@ UniValue importprivkey(const JSONRPCRequest& request)
UniValue abortrescan(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() > 0)
throw std::runtime_error(
RPCHelpMan{"abortrescan",
@ -206,6 +198,10 @@ UniValue abortrescan(const JSONRPCRequest& request)
},
}.ToString());
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
if (!pwallet->IsScanning() || pwallet->IsAbortingRescan()) return false;
pwallet->AbortRescan();
return true;
@ -251,12 +247,6 @@ static void ImportAddress(CWallet * const pwallet, const CTxDestination& dest, c
UniValue importaddress(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() < 1 || request.params.size() > 4)
throw std::runtime_error(
RPCHelpMan{"importaddress",
@ -284,6 +274,9 @@ UniValue importaddress(const JSONRPCRequest& request)
},
}.ToString());
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
std::string strLabel;
if (!request.params[1].isNull())
@ -343,12 +336,6 @@ UniValue importaddress(const JSONRPCRequest& request)
UniValue importprunedfunds(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() != 2)
throw std::runtime_error(
RPCHelpMan{"importprunedfunds",
@ -362,6 +349,10 @@ UniValue importprunedfunds(const JSONRPCRequest& request)
}.ToString()
);
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
CMutableTransaction tx;
if (!DecodeHexTx(tx, request.params[0].get_str()))
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
@ -412,12 +403,6 @@ UniValue importprunedfunds(const JSONRPCRequest& request)
UniValue removeprunedfunds(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
RPCHelpMan{"removeprunedfunds",
@ -433,6 +418,10 @@ UniValue removeprunedfunds(const JSONRPCRequest& request)
},
}.ToString());
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
@ -455,12 +444,6 @@ UniValue removeprunedfunds(const JSONRPCRequest& request)
UniValue importpubkey(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
throw std::runtime_error(
RPCHelpMan{"importpubkey",
@ -484,6 +467,9 @@ UniValue importpubkey(const JSONRPCRequest& request)
},
}.ToString());
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
std::string strLabel;
if (!request.params[1].isNull())
@ -536,12 +522,6 @@ UniValue importpubkey(const JSONRPCRequest& request)
UniValue importwallet(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
RPCHelpMan{"importwallet",
@ -561,6 +541,10 @@ UniValue importwallet(const JSONRPCRequest& request)
},
}.ToString());
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
if (pwallet->chain().havePruned()) {
// Exit early and print an error.
// If a block is pruned after this check, we will import the key(s),
@ -650,7 +634,7 @@ UniValue importwallet(const JSONRPCRequest& request)
std::string label = std::get<3>(key_tuple);
CPubKey pubkey = key.GetPubKey();
assert(key.VerifyPubKey(pubkey));
CHECK_NONFATAL(key.VerifyPubKey(pubkey));
CKeyID keyid = pubkey.GetID();
if (pwallet->HaveKey(keyid)) {
pwallet->WalletLogPrintf("Skipping import of %s (key already present)\n", EncodeDestination(keyid));
@ -702,11 +686,6 @@ UniValue importwallet(const JSONRPCRequest& request)
UniValue importelectrumwallet(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
return NullUniValue;
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
RPCHelpMan{"importselectrumwallet",
@ -726,6 +705,10 @@ UniValue importelectrumwallet(const JSONRPCRequest& request)
},
}.ToString());
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
if (fPruneMode)
throw JSONRPCError(RPC_WALLET_ERROR, "Importing wallets is disabled in pruned mode");
@ -777,7 +760,7 @@ UniValue importelectrumwallet(const JSONRPCRequest& request)
continue;
}
CPubKey pubkey = key.GetPubKey();
assert(key.VerifyPubKey(pubkey));
CHECK_NONFATAL(key.VerifyPubKey(pubkey));
CKeyID keyid = pubkey.GetID();
if (pwallet->HaveKey(keyid)) {
pwallet->WalletLogPrintf("Skipping import of %s (key already present)\n", EncodeDestination(keyid));
@ -809,7 +792,7 @@ UniValue importelectrumwallet(const JSONRPCRequest& request)
continue;
}
CPubKey pubkey = key.GetPubKey();
assert(key.VerifyPubKey(pubkey));
CHECK_NONFATAL(key.VerifyPubKey(pubkey));
CKeyID keyid = pubkey.GetID();
if (pwallet->HaveKey(keyid)) {
pwallet->WalletLogPrintf("Skipping import of %s (key already present)\n", EncodeDestination(keyid));
@ -853,12 +836,6 @@ UniValue importelectrumwallet(const JSONRPCRequest& request)
UniValue dumpprivkey(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
RPCHelpMan{"dumpprivkey",
@ -868,7 +845,7 @@ UniValue dumpprivkey(const JSONRPCRequest& request)
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The dash address for the private key"},
},
RPCResult{
"\"key\" (string) The private key\n"
RPCResult::Type::STR, "key", "The private key"
},
RPCExamples{
HelpExampleCli("dumpprivkey", "\"myaddress\"")
@ -877,6 +854,10 @@ UniValue dumpprivkey(const JSONRPCRequest& request)
},
}.ToString());
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
@ -900,11 +881,6 @@ UniValue dumpprivkey(const JSONRPCRequest& request)
UniValue dumphdinfo(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
return NullUniValue;
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
RPCHelpMan{"dumphdinfo",
@ -923,6 +899,10 @@ UniValue dumphdinfo(const JSONRPCRequest& request)
},
}.ToString());
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
LOCK(pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet);
@ -948,12 +928,6 @@ UniValue dumphdinfo(const JSONRPCRequest& request)
UniValue dumpwallet(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
RPCHelpMan{"dumpwallet",
@ -966,7 +940,7 @@ UniValue dumpwallet(const JSONRPCRequest& request)
},
RPCResult{
"{ (json object)\n"
" \"keys\" : { (int) The number of keys contained in the wallet dump\n"
" \"keys\" : { (numeric) The number of keys contained in the wallet dump\n"
" \"filename\" : { (string) The filename with full absolute path\n"
" \"warning\" : { (string) A warning about not sharing the wallet dump with anyone\n"
"}\n"
@ -977,6 +951,10 @@ UniValue dumpwallet(const JSONRPCRequest& request)
},
}.ToString());
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
auto locked_chain = pwallet->chain().lock();
LockAnnotation lock(::cs_main);
LOCK(pwallet->cs_wallet);
@ -1153,7 +1131,7 @@ static std::string RecurseImportData(const CScript& script, ImportData& import_d
}
case TX_SCRIPTHASH: {
if (script_ctx == ScriptContext::P2SH) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Trying to nest P2SH inside another P2SH");
assert(script_ctx == ScriptContext::TOP);
CHECK_NONFATAL(script_ctx == ScriptContext::TOP);
CScriptID id = CScriptID(uint160(solverdata[0]));
auto subscript = std::move(import_data.redeemscript); // Remove redeemscript from import_data to check for superfluous script later.
if (!subscript) return "missing redeemscript";
@ -1487,12 +1465,6 @@ static int64_t GetImportTimestamp(const UniValue& data, int64_t now)
UniValue importmulti(const JSONRPCRequest& mainRequest)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(mainRequest);
CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, mainRequest.fHelp)) {
return NullUniValue;
}
if (mainRequest.fHelp || mainRequest.params.size() < 1 || mainRequest.params.size() > 2)
throw std::runtime_error(
RPCHelpMan{"importmulti",
@ -1562,6 +1534,10 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
const UniValue& requests = mainRequest.params[0];
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(mainRequest);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
//Default options
bool fRescan = true;

File diff suppressed because it is too large Load Diff

View File

@ -31,9 +31,8 @@ void RegisterWalletRPCCommands(interfaces::Chain& chain, std::vector<std::unique
*/
std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& request);
std::string HelpRequiringPassphrase(CWallet *);
std::string HelpRequiringPassphrase();
void EnsureWalletIsUnlocked(CWallet *);
bool EnsureWalletIsAvailable(CWallet *, bool avoidException);
UniValue getaddressinfo(const JSONRPCRequest& request);
UniValue signrawtransactionwithwallet(const JSONRPCRequest& request);

View File

@ -21,14 +21,15 @@ UniValue getzmqnotifications(const JSONRPCRequest& request)
"\nReturns information about the active ZeroMQ notifications.\n",
{},
RPCResult{
"[\n"
" { (json object)\n"
" \"type\": \"pubhashtx\", (string) Type of notification\n"
" \"address\": \"...\", (string) Address of the publisher\n"
" \"hwm\": n (numeric) Outbound message high water mark\n"
" },\n"
" ...\n"
"]\n"
RPCResult::Type::ARR, "", "",
{
{RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::STR, "type", "Type of notification"},
{RPCResult::Type::STR, "address", "Address of the publisher"},
{RPCResult::Type::NUM, "hwm", "Outbound message high water mark"},
}},
}
},
RPCExamples{
HelpExampleCli("getzmqnotifications", "")

View File

@ -17,6 +17,8 @@ class HelpRpcTest(BitcoinTestFramework):
def run_test(self):
self.test_categories()
self.dump_help()
if self.is_wallet_compiled():
self.wallet_help()
def test_categories(self):
node = self.nodes[0]
@ -52,6 +54,11 @@ class HelpRpcTest(BitcoinTestFramework):
# Make sure the node can generate the help at runtime without crashing
f.write(self.nodes[0].help(call))
def wallet_help(self):
assert 'getnewaddress ( "label" )' in self.nodes[0].help('getnewaddress')
self.restart_node(0, extra_args=['-nowallet=1'])
assert 'getnewaddress ( "label" )' in self.nodes[0].help('getnewaddress')
if __name__ == '__main__':
HelpRpcTest().main()

View File

@ -23,6 +23,13 @@ class RpcMiscTest(BitcoinTestFramework):
def run_test(self):
node = self.nodes[0]
self.log.info("test CHECK_NONFATAL")
assert_raises_rpc_error(
-1,
"Internal bug detected: 'request.params.size() != 100'",
lambda: node.echo(*[0] * 100),
)
self.log.info("test getmemoryinfo")
memory = node.getmemoryinfo()['locked']
assert_greater_than(memory['used'], 0)

View File

@ -6,7 +6,7 @@
from decimal import Decimal
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_array_result
from test_framework.util import assert_array_result, assert_equal
class ListTransactionsTest(BitcoinTestFramework):
def set_test_params(self):
@ -83,6 +83,8 @@ class ListTransactionsTest(BitcoinTestFramework):
txid = self.nodes[1].sendtoaddress(multisig["address"], 0.1)
self.nodes[1].generate(1)
self.sync_all()
assert_equal(len(self.nodes[0].listtransactions(label="watchonly", include_watchonly=True)), 1)
assert_equal(len(self.nodes[0].listtransactions(dummy="watchonly", include_watchonly=True)), 1)
assert len(self.nodes[0].listtransactions(label="watchonly", count=100, include_watchonly=False)) == 0
assert_array_result(self.nodes[0].listtransactions(label="watchonly", count=100, include_watchonly=True),
{"category": "receive", "amount": Decimal("0.1")},

View File

@ -20,4 +20,15 @@ if [[ ${OUTPUT} != "" ]]; then
EXIT_CODE=1
fi
# Macro CHECK_NONFATAL(condition) should be used instead of assert for RPC code, where it
# is undesirable to crash the whole program. See: src/util/check.h
# src/rpc/server.cpp is excluded from this check since it's mostly meta-code.
OUTPUT=$(git grep -nE 'assert *\(.*\);' -- "src/rpc/" "src/wallet/rpc*" ":(exclude)src/rpc/server.cpp")
if [[ ${OUTPUT} != "" ]]; then
echo "CHECK_NONFATAL(condition) should be used instead of assert for RPC code."
echo
echo "${OUTPUT}"
EXIT_CODE=1
fi
exit ${EXIT_CODE}