diff options
Diffstat (limited to 'src/rpc')
-rw-r--r-- | src/rpc/blockchain.cpp | 449 | ||||
-rw-r--r-- | src/rpc/blockchain.h | 3 | ||||
-rw-r--r-- | src/rpc/client.cpp | 18 | ||||
-rw-r--r-- | src/rpc/mining.cpp | 232 | ||||
-rw-r--r-- | src/rpc/misc.cpp | 179 | ||||
-rw-r--r-- | src/rpc/net.cpp | 182 | ||||
-rw-r--r-- | src/rpc/protocol.cpp | 18 | ||||
-rw-r--r-- | src/rpc/rawtransaction.cpp | 623 | ||||
-rw-r--r-- | src/rpc/rawtransaction.h | 6 | ||||
-rw-r--r-- | src/rpc/server.cpp | 78 | ||||
-rw-r--r-- | src/rpc/server.h | 4 | ||||
-rw-r--r-- | src/rpc/util.cpp | 305 | ||||
-rw-r--r-- | src/rpc/util.h | 99 |
13 files changed, 1336 insertions, 860 deletions
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 51bc218d39..ec87f42c93 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -12,23 +12,26 @@ #include <checkpoints.h> #include <coins.h> #include <consensus/validation.h> -#include <validation.h> #include <core_io.h> +#include <hash.h> #include <index/txindex.h> #include <key_io.h> #include <policy/feerate.h> #include <policy/policy.h> +#include <policy/rbf.h> #include <primitives/transaction.h> #include <rpc/server.h> +#include <rpc/util.h> #include <script/descriptor.h> #include <streams.h> #include <sync.h> #include <txdb.h> #include <txmempool.h> -#include <util.h> -#include <utilstrencodings.h> -#include <hash.h> +#include <util/strencodings.h> +#include <util/system.h> +#include <validation.h> #include <validationinterface.h> +#include <versionbitsinfo.h> #include <warnings.h> #include <assert.h> @@ -36,7 +39,6 @@ #include <univalue.h> -#include <boost/algorithm/string.hpp> #include <boost/thread/thread.hpp> // boost::thread::interrupt #include <memory> @@ -49,7 +51,7 @@ struct CUpdatedBlock int height; }; -static std::mutex cs_blockchange; +static Mutex cs_blockchange; static std::condition_variable cond_blockchange; static CUpdatedBlock latestblock; @@ -120,8 +122,8 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx if (chainActive.Contains(blockindex)) confirmations = chainActive.Height() - blockindex->nHeight + 1; result.pushKV("confirmations", confirmations); - result.pushKV("strippedsize", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS)); - result.pushKV("size", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION)); + result.pushKV("strippedsize", (int)::GetSerializeSize(block, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS)); + result.pushKV("size", (int)::GetSerializeSize(block, PROTOCOL_VERSION)); result.pushKV("weight", (int)::GetBlockWeight(block)); result.pushKV("height", blockindex->nHeight); result.pushKV("version", block.nVersion); @@ -160,8 +162,9 @@ static UniValue getblockcount(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 0) throw std::runtime_error( - "getblockcount\n" - "\nReturns the number of blocks in the longest blockchain.\n" + RPCHelpMan{"getblockcount", + "\nReturns the number of blocks in the longest blockchain.\n", {}} + .ToString() + "\nResult:\n" "n (numeric) The current block count\n" "\nExamples:\n" @@ -177,10 +180,11 @@ static UniValue getbestblockhash(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 0) throw std::runtime_error( - "getbestblockhash\n" - "\nReturns the hash of the best (tip) block in the longest blockchain.\n" + RPCHelpMan{"getbestblockhash", + "\nReturns the hash of the best (tip) block in the longest blockchain.\n", {}} + .ToString() + "\nResult:\n" - "\"hex\" (string) the block hash hex encoded\n" + "\"hex\" (string) the block hash, hex-encoded\n" "\nExamples:\n" + HelpExampleCli("getbestblockhash", "") + HelpExampleRpc("getbestblockhash", "") @@ -204,11 +208,13 @@ static UniValue waitfornewblock(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() > 1) throw std::runtime_error( - "waitfornewblock (timeout)\n" - "\nWaits for a specific new block and returns useful info about it.\n" - "\nReturns the current block on timeout or exit.\n" - "\nArguments:\n" - "1. timeout (int, optional, default=0) Time in milliseconds to wait for a response. 0 indicates no timeout.\n" + RPCHelpMan{"waitfornewblock", + "\nWaits for a specific new block and returns useful info about it.\n" + "\nReturns the current block on timeout or exit.\n", + { + {"timeout", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "0", "Time in milliseconds to wait for a response. 0 indicates no timeout."}, + }} + .ToString() + "\nResult:\n" "{ (json object)\n" " \"hash\" : { (string) The blockhash\n" @@ -224,7 +230,7 @@ static UniValue waitfornewblock(const JSONRPCRequest& request) CUpdatedBlock block; { - std::unique_lock<std::mutex> lock(cs_blockchange); + WAIT_LOCK(cs_blockchange, lock); block = latestblock; if(timeout) cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&block]{return latestblock.height != block.height || latestblock.hash != block.hash || !IsRPCRunning(); }); @@ -242,12 +248,14 @@ static UniValue waitforblock(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) throw std::runtime_error( - "waitforblock <blockhash> (timeout)\n" - "\nWaits for a specific new block and returns useful info about it.\n" - "\nReturns the current block on timeout or exit.\n" - "\nArguments:\n" - "1. \"blockhash\" (required, string) Block hash to wait for.\n" - "2. timeout (int, optional, default=0) Time in milliseconds to wait for a response. 0 indicates no timeout.\n" + RPCHelpMan{"waitforblock", + "\nWaits for a specific new block and returns useful info about it.\n" + "\nReturns the current block on timeout or exit.\n", + { + {"blockhash", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "Block hash to wait for."}, + {"timeout", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "0", "Time in milliseconds to wait for a response. 0 indicates no timeout."}, + }} + .ToString() + "\nResult:\n" "{ (json object)\n" " \"hash\" : { (string) The blockhash\n" @@ -259,14 +267,14 @@ static UniValue waitforblock(const JSONRPCRequest& request) ); int timeout = 0; - uint256 hash = uint256S(request.params[0].get_str()); + uint256 hash(ParseHashV(request.params[0], "blockhash")); if (!request.params[1].isNull()) timeout = request.params[1].get_int(); CUpdatedBlock block; { - std::unique_lock<std::mutex> lock(cs_blockchange); + WAIT_LOCK(cs_blockchange, lock); if(timeout) cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&hash]{return latestblock.hash == hash || !IsRPCRunning();}); else @@ -284,13 +292,15 @@ static UniValue waitforblockheight(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) throw std::runtime_error( - "waitforblockheight <height> (timeout)\n" - "\nWaits for (at least) block height and returns the height and hash\n" - "of the current tip.\n" - "\nReturns the current block on timeout or exit.\n" - "\nArguments:\n" - "1. height (required, int) Block height to wait for (int)\n" - "2. timeout (int, optional, default=0) Time in milliseconds to wait for a response. 0 indicates no timeout.\n" + RPCHelpMan{"waitforblockheight", + "\nWaits for (at least) block height and returns the height and hash\n" + "of the current tip.\n" + "\nReturns the current block on timeout or exit.\n", + { + {"height", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "Block height to wait for."}, + {"timeout", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "0", "Time in milliseconds to wait for a response. 0 indicates no timeout."}, + }} + .ToString() + "\nResult:\n" "{ (json object)\n" " \"hash\" : { (string) The blockhash\n" @@ -309,7 +319,7 @@ static UniValue waitforblockheight(const JSONRPCRequest& request) CUpdatedBlock block; { - std::unique_lock<std::mutex> lock(cs_blockchange); + WAIT_LOCK(cs_blockchange, lock); if(timeout) cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&height]{return latestblock.height >= height || !IsRPCRunning();}); else @@ -326,8 +336,9 @@ static UniValue syncwithvalidationinterfacequeue(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() > 0) { throw std::runtime_error( - "syncwithvalidationinterfacequeue\n" - "\nWaits for the validation interface queue to catch up on everything that was there when we entered this function.\n" + RPCHelpMan{"syncwithvalidationinterfacequeue", + "\nWaits for the validation interface queue to catch up on everything that was there when we entered this function.\n", {}} + .ToString() + "\nExamples:\n" + HelpExampleCli("syncwithvalidationinterfacequeue","") + HelpExampleRpc("syncwithvalidationinterfacequeue","") @@ -341,8 +352,9 @@ static UniValue getdifficulty(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 0) throw std::runtime_error( - "getdifficulty\n" - "\nReturns the proof-of-work difficulty as a multiple of the minimum difficulty.\n" + RPCHelpMan{"getdifficulty", + "\nReturns the proof-of-work difficulty as a multiple of the minimum difficulty.\n", {}} + .ToString() + "\nResult:\n" "n.nnn (numeric) the proof-of-work difficulty as a multiple of the minimum difficulty.\n" "\nExamples:\n" @@ -379,7 +391,8 @@ static std::string EntryDescriptionString() " ... ]\n" " \"spentby\" : [ (array) unconfirmed transactions spending outputs from this transaction\n" " \"transactionid\", (string) child transaction id\n" - " ... ]\n"; + " ... ]\n" + " \"bip125-replaceable\" : true|false, (boolean) Whether this transaction could be replaced due to BIP125 (replace-by-fee)\n"; } static void entryToJSON(UniValue &info, const CTxMemPoolEntry &e) EXCLUSIVE_LOCKS_REQUIRED(::mempool.cs) @@ -424,11 +437,22 @@ static void entryToJSON(UniValue &info, const CTxMemPoolEntry &e) EXCLUSIVE_LOCK UniValue spent(UniValue::VARR); const CTxMemPool::txiter &it = mempool.mapTx.find(tx.GetHash()); const CTxMemPool::setEntries &setChildren = mempool.GetMemPoolChildren(it); - for (const CTxMemPool::txiter &childiter : setChildren) { + for (CTxMemPool::txiter childiter : setChildren) { spent.push_back(childiter->GetTx().GetHash().ToString()); } info.pushKV("spentby", spent); + + // Add opt-in RBF status + bool rbfStatus = false; + RBFTransactionState rbfState = IsRBFOptIn(tx, mempool); + if (rbfState == RBFTransactionState::UNKNOWN) { + throw JSONRPCError(RPC_MISC_ERROR, "Transaction is not in mempool"); + } else if (rbfState == RBFTransactionState::REPLACEABLE_BIP125) { + rbfStatus = true; + } + + info.pushKV("bip125-replaceable", rbfStatus); } UniValue mempoolToJSON(bool fVerbose) @@ -463,11 +487,13 @@ static UniValue getrawmempool(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() > 1) throw std::runtime_error( - "getrawmempool ( verbose )\n" - "\nReturns all transaction ids in memory pool as a json array of string transaction ids.\n" - "\nHint: use getmempoolentry to fetch a specific transaction from the mempool.\n" - "\nArguments:\n" - "1. verbose (boolean, optional, default=false) True for a json object, false for array of transaction ids\n" + RPCHelpMan{"getrawmempool", + "\nReturns all transaction ids in memory pool as a json array of string transaction ids.\n" + "\nHint: use getmempoolentry to fetch a specific transaction from the mempool.\n", + { + {"verbose", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "True for a json object, false for array of transaction ids"}, + }} + .ToString() + "\nResult: (for verbose = false):\n" "[ (json array of string)\n" " \"transactionid\" (string) The transaction id\n" @@ -495,17 +521,19 @@ static UniValue getmempoolancestors(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { throw std::runtime_error( - "getmempoolancestors txid (verbose)\n" - "\nIf txid is in the mempool, returns all in-mempool ancestors.\n" - "\nArguments:\n" - "1. \"txid\" (string, required) The transaction id (must be in mempool)\n" - "2. verbose (boolean, optional, default=false) True for a json object, false for array of transaction ids\n" - "\nResult (for verbose=false):\n" + RPCHelpMan{"getmempoolancestors", + "\nIf txid is in the mempool, returns all in-mempool ancestors.\n", + { + {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The transaction id (must be in mempool)"}, + {"verbose", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "True for a json object, false for array of transaction ids"}, + }} + .ToString() + + "\nResult (for verbose = false):\n" "[ (json array of strings)\n" " \"transactionid\" (string) The transaction id of an in-mempool ancestor transaction\n" " ,...\n" "]\n" - "\nResult (for verbose=true):\n" + "\nResult (for verbose = true):\n" "{ (json object)\n" " \"transactionid\" : { (json object)\n" + EntryDescriptionString() @@ -559,17 +587,19 @@ static UniValue getmempooldescendants(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { throw std::runtime_error( - "getmempooldescendants txid (verbose)\n" - "\nIf txid is in the mempool, returns all in-mempool descendants.\n" - "\nArguments:\n" - "1. \"txid\" (string, required) The transaction id (must be in mempool)\n" - "2. verbose (boolean, optional, default=false) True for a json object, false for array of transaction ids\n" - "\nResult (for verbose=false):\n" + RPCHelpMan{"getmempooldescendants", + "\nIf txid is in the mempool, returns all in-mempool descendants.\n", + { + {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The transaction id (must be in mempool)"}, + {"verbose", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "True for a json object, false for array of transaction ids"}, + }} + .ToString() + + "\nResult (for verbose = false):\n" "[ (json array of strings)\n" " \"transactionid\" (string) The transaction id of an in-mempool descendant transaction\n" " ,...\n" "]\n" - "\nResult (for verbose=true):\n" + "\nResult (for verbose = true):\n" "{ (json object)\n" " \"transactionid\" : { (json object)\n" + EntryDescriptionString() @@ -623,10 +653,12 @@ static UniValue getmempoolentry(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( - "getmempoolentry txid\n" - "\nReturns mempool data for given transaction\n" - "\nArguments:\n" - "1. \"txid\" (string, required) The transaction id (must be in mempool)\n" + RPCHelpMan{"getmempoolentry", + "\nReturns mempool data for given transaction\n", + { + {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The transaction id (must be in mempool)"}, + }} + .ToString() + "\nResult:\n" "{ (json object)\n" + EntryDescriptionString() @@ -656,10 +688,12 @@ static UniValue getblockhash(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 1) throw std::runtime_error( - "getblockhash height\n" - "\nReturns hash of block in best-block-chain at height provided.\n" - "\nArguments:\n" - "1. height (numeric, required) The height index\n" + RPCHelpMan{"getblockhash", + "\nReturns hash of block in best-block-chain at height provided.\n", + { + {"height", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "The height index"}, + }} + .ToString() + "\nResult:\n" "\"hash\" (string) The block hash\n" "\nExamples:\n" @@ -681,12 +715,14 @@ static UniValue getblockheader(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) throw std::runtime_error( - "getblockheader \"hash\" ( verbose )\n" - "\nIf verbose is false, returns a string that is serialized, hex-encoded data for blockheader 'hash'.\n" - "If verbose is true, returns an Object with information about blockheader <hash>.\n" - "\nArguments:\n" - "1. \"hash\" (string, required) The block hash\n" - "2. verbose (boolean, optional, default=true) true for a json object, false for the hex encoded data\n" + RPCHelpMan{"getblockheader", + "\nIf verbose is false, returns a string that is serialized, hex-encoded data for blockheader 'hash'.\n" + "If verbose is true, returns an Object with information about blockheader <hash>.\n", + { + {"blockhash", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The block hash"}, + {"verbose", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "true", "true for a json object, false for the hex-encoded data"}, + }} + .ToString() + "\nResult (for verbose = true):\n" "{\n" " \"hash\" : \"hash\", (string) the block hash (same as provided)\n" @@ -714,8 +750,7 @@ static UniValue getblockheader(const JSONRPCRequest& request) LOCK(cs_main); - std::string strHash = request.params[0].get_str(); - uint256 hash(uint256S(strHash)); + uint256 hash(ParseHashV(request.params[0], "hash")); bool fVerbose = true; if (!request.params[1].isNull()) @@ -760,13 +795,15 @@ static UniValue getblock(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) throw std::runtime_error( - "getblock \"blockhash\" ( verbosity ) \n" - "\nIf verbosity is 0, returns a string that is serialized, hex-encoded data for block 'hash'.\n" - "If verbosity is 1, returns an Object with information about block <hash>.\n" - "If verbosity is 2, returns an Object with information about block <hash> and information about each transaction. \n" - "\nArguments:\n" - "1. \"blockhash\" (string, required) The block hash\n" - "2. verbosity (numeric, optional, default=1) 0 for hex encoded data, 1 for a json object, and 2 for json object with transaction data\n" + RPCHelpMan{"getblock", + "\nIf verbosity is 0, returns a string that is serialized, hex-encoded data for block 'hash'.\n" + "If verbosity is 1, returns an Object with information about block <hash>.\n" + "If verbosity is 2, returns an Object with information about block <hash> and information about each transaction. \n", + { + {"blockhash", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The block hash"}, + {"verbosity", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "1", "0 for hex-encoded data, 1 for a json object, and 2 for json object with transaction data"}, + }} + .ToString() + "\nResult (for verbosity = 0):\n" "\"data\" (string) A string that is serialized, hex-encoded data for block 'hash'.\n" "\nResult (for verbosity = 1):\n" @@ -809,8 +846,7 @@ static UniValue getblock(const JSONRPCRequest& request) LOCK(cs_main); - std::string strHash = request.params[0].get_str(); - uint256 hash(uint256S(strHash)); + uint256 hash(ParseHashV(request.params[0], "blockhash")); int verbosity = 1; if (!request.params[1].isNull()) { @@ -913,10 +949,12 @@ static UniValue pruneblockchain(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 1) throw std::runtime_error( - "pruneblockchain\n" - "\nArguments:\n" - "1. \"height\" (numeric, required) The block height to prune up to. May be set to a discrete height, or a unix timestamp\n" - " to prune blocks whose block time is at least 2 hours older than the provided timestamp.\n" + RPCHelpMan{"pruneblockchain", "", + { + {"height", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "The block height to prune up to. May be set to a discrete height, or a unix timestamp\n" + " to prune blocks whose block time is at least 2 hours older than the provided timestamp."}, + }} + .ToString() + "\nResult:\n" "n (numeric) Height of the last block pruned.\n" "\nExamples:\n" @@ -962,9 +1000,11 @@ static UniValue gettxoutsetinfo(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 0) throw std::runtime_error( - "gettxoutsetinfo\n" - "\nReturns statistics about the unspent transaction output set.\n" - "Note this call may take some time.\n" + RPCHelpMan{"gettxoutsetinfo", + "\nReturns statistics about the unspent transaction output set.\n" + "Note this call may take some time.\n", + {}} + .ToString() + "\nResult:\n" "{\n" " \"height\":n, (numeric) The current block height (index)\n" @@ -1004,13 +1044,14 @@ UniValue gettxout(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 2 || request.params.size() > 3) throw std::runtime_error( - "gettxout \"txid\" n ( include_mempool )\n" - "\nReturns details about an unspent transaction output.\n" - "\nArguments:\n" - "1. \"txid\" (string, required) The transaction id\n" - "2. \"n\" (numeric, required) vout number\n" - "3. \"include_mempool\" (boolean, optional) Whether to include the mempool. Default: true." - " Note that an unspent output that is spent in the mempool won't appear.\n" + RPCHelpMan{"gettxout", + "\nReturns details about an unspent transaction output.\n", + { + {"txid", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The transaction id"}, + {"n", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "vout number"}, + {"include_mempool", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "true", "Whether to include the mempool. Note that an unspent output that is spent in the mempool won't appear."}, + }} + .ToString() + "\nResult:\n" "{\n" " \"bestblock\": \"hash\", (string) The hash of the block at the tip of the chain\n" @@ -1034,7 +1075,7 @@ UniValue gettxout(const JSONRPCRequest& request) + HelpExampleCli("listunspent", "") + "\nView the details\n" + HelpExampleCli("gettxout", "\"txid\" 1") + - "\nAs a json rpc call\n" + "\nAs a JSON-RPC call\n" + HelpExampleRpc("gettxout", "\"txid\", 1") ); @@ -1042,8 +1083,7 @@ UniValue gettxout(const JSONRPCRequest& request) UniValue ret(UniValue::VOBJ); - std::string strHash = request.params[0].get_str(); - uint256 hash(uint256S(strHash)); + uint256 hash(ParseHashV(request.params[0], "txid")); int n = request.params[1].get_int(); COutPoint out(hash, n); bool fMempool = true; @@ -1085,11 +1125,13 @@ static UniValue verifychain(const JSONRPCRequest& request) int nCheckDepth = gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS); if (request.fHelp || request.params.size() > 2) throw std::runtime_error( - "verifychain ( checklevel nblocks )\n" - "\nVerifies blockchain database.\n" - "\nArguments:\n" - "1. checklevel (numeric, optional, 0-4, default=" + strprintf("%d", nCheckLevel) + ") How thorough the block verification is.\n" - "2. nblocks (numeric, optional, default=" + strprintf("%d", nCheckDepth) + ", 0=all) The number of blocks to check.\n" + RPCHelpMan{"verifychain", + "\nVerifies blockchain database.\n", + { + {"checklevel", RPCArg::Type::NUM, /* opt */ true, /* default_val */ strprintf("%d, range=0-4", nCheckLevel), "How thorough the block verification is."}, + {"nblocks", RPCArg::Type::NUM, /* opt */ true, /* default_val */ strprintf("%d, 0=all", nCheckDepth), "The number of blocks to check."}, + }} + .ToString() + "\nResult:\n" "true|false (boolean) Verified or not\n" "\nExamples:\n" @@ -1182,8 +1224,9 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 0) throw std::runtime_error( - "getblockchaininfo\n" - "Returns an object containing various state info regarding blockchain processing.\n" + RPCHelpMan{"getblockchaininfo", + "Returns an object containing various state info regarding blockchain processing.\n", {}} + .ToString() + "\nResult:\n" "{\n" " \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n" @@ -1299,9 +1342,11 @@ static UniValue getchaintips(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 0) throw std::runtime_error( - "getchaintips\n" - "Return information about all known tips in the block tree," - " including the main chain as well as orphaned branches.\n" + RPCHelpMan{"getchaintips", + "Return information about all known tips in the block tree," + " including the main chain as well as orphaned branches.\n", + {}} + .ToString() + "\nResult:\n" "[\n" " {\n" @@ -1377,7 +1422,7 @@ static UniValue getchaintips(const JSONRPCRequest& request) } else if (block->nStatus & BLOCK_FAILED_MASK) { // This block or one of its ancestors is invalid. status = "invalid"; - } else if (block->nChainTx == 0) { + } else if (!block->HaveTxsDownloaded()) { // This block cannot be connected because full block data for it or one of its parents is missing. status = "headers-only"; } else if (block->IsValid(BLOCK_VALID_SCRIPTS)) { @@ -1416,8 +1461,9 @@ static UniValue getmempoolinfo(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 0) throw std::runtime_error( - "getmempoolinfo\n" - "\nReturns details on the active state of the TX memory pool.\n" + RPCHelpMan{"getmempoolinfo", + "\nReturns details on the active state of the TX memory pool.\n", {}} + .ToString() + "\nResult:\n" "{\n" " \"size\": xxxxx, (numeric) Current tx count\n" @@ -1439,20 +1485,21 @@ static UniValue preciousblock(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 1) throw std::runtime_error( - "preciousblock \"blockhash\"\n" - "\nTreats a block as if it were received before others with the same work.\n" - "\nA later preciousblock call can override the effect of an earlier one.\n" - "\nThe effects of preciousblock are not retained across restarts.\n" - "\nArguments:\n" - "1. \"blockhash\" (string, required) the hash of the block to mark as precious\n" + RPCHelpMan{"preciousblock", + "\nTreats a block as if it were received before others with the same work.\n" + "\nA later preciousblock call can override the effect of an earlier one.\n" + "\nThe effects of preciousblock are not retained across restarts.\n", + { + {"blockhash", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "the hash of the block to mark as precious"}, + }} + .ToString() + "\nResult:\n" "\nExamples:\n" + HelpExampleCli("preciousblock", "\"blockhash\"") + HelpExampleRpc("preciousblock", "\"blockhash\"") ); - std::string strHash = request.params[0].get_str(); - uint256 hash(uint256S(strHash)); + uint256 hash(ParseHashV(request.params[0], "blockhash")); CBlockIndex* pblockindex; { @@ -1477,18 +1524,19 @@ static UniValue invalidateblock(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 1) throw std::runtime_error( - "invalidateblock \"blockhash\"\n" - "\nPermanently marks a block as invalid, as if it violated a consensus rule.\n" - "\nArguments:\n" - "1. \"blockhash\" (string, required) the hash of the block to mark as invalid\n" + RPCHelpMan{"invalidateblock", + "\nPermanently marks a block as invalid, as if it violated a consensus rule.\n", + { + {"blockhash", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "the hash of the block to mark as invalid"}, + }} + .ToString() + "\nResult:\n" "\nExamples:\n" + HelpExampleCli("invalidateblock", "\"blockhash\"") + HelpExampleRpc("invalidateblock", "\"blockhash\"") ); - std::string strHash = request.params[0].get_str(); - uint256 hash(uint256S(strHash)); + uint256 hash(ParseHashV(request.params[0], "blockhash")); CValidationState state; { @@ -1516,19 +1564,20 @@ static UniValue reconsiderblock(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 1) throw std::runtime_error( - "reconsiderblock \"blockhash\"\n" - "\nRemoves invalidity status of a block and its descendants, reconsider them for activation.\n" - "This can be used to undo the effects of invalidateblock.\n" - "\nArguments:\n" - "1. \"blockhash\" (string, required) the hash of the block to reconsider\n" + RPCHelpMan{"reconsiderblock", + "\nRemoves invalidity status of a block and its descendants, reconsider them for activation.\n" + "This can be used to undo the effects of invalidateblock.\n", + { + {"blockhash", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "the hash of the block to reconsider"}, + }} + .ToString() + "\nResult:\n" "\nExamples:\n" + HelpExampleCli("reconsiderblock", "\"blockhash\"") + HelpExampleRpc("reconsiderblock", "\"blockhash\"") ); - std::string strHash = request.params[0].get_str(); - uint256 hash(uint256S(strHash)); + uint256 hash(ParseHashV(request.params[0], "blockhash")); { LOCK(cs_main); @@ -1554,11 +1603,13 @@ static UniValue getchaintxstats(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() > 2) throw std::runtime_error( - "getchaintxstats ( nblocks blockhash )\n" - "\nCompute statistics about the total number and rate of transactions in the chain.\n" - "\nArguments:\n" - "1. nblocks (numeric, optional) Size of the window in number of blocks (default: one month).\n" - "2. \"blockhash\" (string, optional) The hash of the block that ends the window.\n" + RPCHelpMan{"getchaintxstats", + "\nCompute statistics about the total number and rate of transactions in the chain.\n", + { + {"nblocks", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "one month", "Size of the window in number of blocks"}, + {"blockhash", RPCArg::Type::STR_HEX, /* opt */ true, /* default_val */ "chain tip", "The hash of the block that ends the window."}, + }} + .ToString() + "\nResult:\n" "{\n" " \"time\": xxxxx, (numeric) The timestamp for the final block in the window in UNIX format.\n" @@ -1581,7 +1632,7 @@ static UniValue getchaintxstats(const JSONRPCRequest& request) LOCK(cs_main); pindex = chainActive.Tip(); } else { - uint256 hash = uint256S(request.params[1].get_str()); + uint256 hash(ParseHashV(request.params[1], "blockhash")); LOCK(cs_main); pindex = LookupBlockIndex(hash); if (!pindex) { @@ -1684,18 +1735,20 @@ static UniValue getblockstats(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 4) { throw std::runtime_error( - "getblockstats hash_or_height ( stats )\n" - "\nCompute per block statistics for a given window. All amounts are in satoshis.\n" - "It won't work for some heights with pruning.\n" - "It won't work without -txindex for utxo_size_inc, *fee or *feerate stats.\n" - "\nArguments:\n" - "1. \"hash_or_height\" (string or numeric, required) The block hash or height of the target block\n" - "2. \"stats\" (array, optional) Values to plot, by default all values (see result below)\n" - " [\n" - " \"height\", (string, optional) Selected statistic\n" - " \"time\", (string, optional) Selected statistic\n" - " ,...\n" - " ]\n" + RPCHelpMan{"getblockstats", + "\nCompute per block statistics for a given window. All amounts are in satoshis.\n" + "It won't work for some heights with pruning.\n" + "It won't work without -txindex for utxo_size_inc, *fee or *feerate stats.\n", + { + {"hash_or_height", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "The block hash or height of the target block", "", {"", "string or numeric"}}, + {"stats", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "all values", "Values to plot (see result below)", + { + {"height", RPCArg::Type::STR, /* opt */ true, /* default_val */ "", "Selected statistic"}, + {"time", RPCArg::Type::STR, /* opt */ true, /* default_val */ "", "Selected statistic"}, + }, + "stats"}, + }} + .ToString() + "\nResult:\n" "{ (json object)\n" " \"avgfee\": xxxxx, (numeric) Average fee in the block\n" @@ -1755,8 +1808,7 @@ static UniValue getblockstats(const JSONRPCRequest& request) pindex = chainActive[height]; } else { - const std::string strHash = request.params[0].get_str(); - const uint256 hash(uint256S(strHash)); + const uint256 hash(ParseHashV(request.params[0], "hash_or_height")); pindex = LookupBlockIndex(hash); if (!pindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); @@ -1791,6 +1843,10 @@ static UniValue getblockstats(const JSONRPCRequest& request) const bool do_calculate_weight = do_all || SetHasKeys(stats, "total_weight", "avgfeerate", "swtotal_weight", "avgfeerate", "feerate_percentiles", "minfeerate", "maxfeerate"); const bool do_calculate_sw = do_all || SetHasKeys(stats, "swtxs", "swtotal_size", "swtotal_weight"); + if (loop_inputs && !g_txindex) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "One or more of the selected stats requires -txindex enabled"); + } + CAmount maxfee = 0; CAmount maxfeerate = 0; CAmount minfee = MAX_MONEY; @@ -1818,7 +1874,7 @@ static UniValue getblockstats(const JSONRPCRequest& request) if (loop_outputs) { for (const CTxOut& out : tx->vout) { tx_total_out += out.nValue; - utxo_size_inc += GetSerializeSize(out, SER_NETWORK, PROTOCOL_VERSION) + PER_UTXO_OVERHEAD; + utxo_size_inc += GetSerializeSize(out, PROTOCOL_VERSION) + PER_UTXO_OVERHEAD; } } @@ -1854,10 +1910,6 @@ static UniValue getblockstats(const JSONRPCRequest& request) } if (loop_inputs) { - - if (!g_txindex) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "One or more of the selected stats requires -txindex enabled"); - } CAmount tx_total_in = 0; for (const CTxIn& in : tx->vin) { CTransactionRef tx_in; @@ -1869,7 +1921,7 @@ static UniValue getblockstats(const JSONRPCRequest& request) CTxOut prevoutput = tx_in->vout[in.prevout.n]; tx_total_in += prevoutput.nValue; - utxo_size_inc -= GetSerializeSize(prevoutput, SER_NETWORK, PROTOCOL_VERSION) + PER_UTXO_OVERHEAD; + utxo_size_inc -= GetSerializeSize(prevoutput, PROTOCOL_VERSION) + PER_UTXO_OVERHEAD; } CAmount txfee = tx_total_in - tx_total_out; @@ -1949,8 +2001,9 @@ static UniValue savemempool(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 0) { throw std::runtime_error( - "savemempool\n" - "\nDumps the mempool to disk. It will fail until the previous dump is fully loaded.\n" + RPCHelpMan{"savemempool", + "\nDumps the mempool to disk. It will fail until the previous dump is fully loaded.\n", {}} + .ToString() + "\nExamples:\n" + HelpExampleCli("savemempool", "") + HelpExampleRpc("savemempool", "") @@ -2032,34 +2085,39 @@ UniValue scantxoutset(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) throw std::runtime_error( - "scantxoutset <action> ( <scanobjects> )\n" - "\nEXPERIMENTAL warning: this call may be removed or changed in future releases.\n" - "\nScans the unspent transaction output set for entries that match certain output descriptors.\n" - "Examples of output descriptors are:\n" - " addr(<address>) Outputs whose scriptPubKey corresponds to the specified address (does not include P2PK)\n" - " raw(<hex script>) Outputs whose scriptPubKey equals the specified hex scripts\n" - " combo(<pubkey>) P2PK, P2PKH, P2WPKH, and P2SH-P2WPKH outputs for the given pubkey\n" - " pkh(<pubkey>) P2PKH outputs for the given pubkey\n" - " sh(multi(<n>,<pubkey>,<pubkey>,...)) P2SH-multisig outputs for the given threshold and pubkeys\n" - "\nIn the above, <pubkey> either refers to a fixed public key in hexadecimal notation, or to an xpub/xprv optionally followed by one\n" - "or more path elements separated by \"/\", and optionally ending in \"/*\" (unhardened), or \"/*'\" or \"/*h\" (hardened) to specify all\n" - "unhardened or hardened child keys.\n" - "In the latter case, a range needs to be specified by below if different from 1000.\n" - "For more information on output descriptors, see the documentation at TODO\n" - "\nArguments:\n" - "1. \"action\" (string, required) The action to execute\n" + RPCHelpMan{"scantxoutset", + "\nEXPERIMENTAL warning: this call may be removed or changed in future releases.\n" + "\nScans the unspent transaction output set for entries that match certain output descriptors.\n" + "Examples of output descriptors are:\n" + " addr(<address>) Outputs whose scriptPubKey corresponds to the specified address (does not include P2PK)\n" + " raw(<hex script>) Outputs whose scriptPubKey equals the specified hex scripts\n" + " combo(<pubkey>) P2PK, P2PKH, P2WPKH, and P2SH-P2WPKH outputs for the given pubkey\n" + " pkh(<pubkey>) P2PKH outputs for the given pubkey\n" + " sh(multi(<n>,<pubkey>,<pubkey>,...)) P2SH-multisig outputs for the given threshold and pubkeys\n" + "\nIn the above, <pubkey> either refers to a fixed public key in hexadecimal notation, or to an xpub/xprv optionally followed by one\n" + "or more path elements separated by \"/\", and optionally ending in \"/*\" (unhardened), or \"/*'\" or \"/*h\" (hardened) to specify all\n" + "unhardened or hardened child keys.\n" + "In the latter case, a range needs to be specified by below if different from 1000.\n" + "For more information on output descriptors, see the documentation in the doc/descriptors.md file.\n", + { + {"action", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The action to execute\n" " \"start\" for starting a scan\n" " \"abort\" for aborting the current scan (returns true when abort was successful)\n" - " \"status\" for progress report (in %) of the current scan\n" - "2. \"scanobjects\" (array, required) Array of scan objects\n" - " [ Every scan object is either a string descriptor or an object:\n" - " \"descriptor\", (string, optional) An output descriptor\n" - " { (object, optional) An object with output descriptor and metadata\n" - " \"desc\": \"descriptor\", (string, required) An output descriptor\n" - " \"range\": n, (numeric, optional) Up to what child index HD chains should be explored (default: 1000)\n" - " },\n" - " ...\n" - " ]\n" + " \"status\" for progress report (in %) of the current scan"}, + {"scanobjects", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "Array of scan objects\n" + " Every scan object is either a string descriptor or an object:", + { + {"descriptor", RPCArg::Type::STR, /* opt */ true, /* default_val */ "", "An output descriptor"}, + {"", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "", "An object with output descriptor and metadata", + { + {"desc", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "An output descriptor"}, + {"range", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "1000", "Up to what child index HD chains should be explored"}, + }, + }, + }, + "[scanobjects,...]"}, + }} + .ToString() + "\nResult:\n" "{\n" " \"unspents\": [\n" @@ -2067,6 +2125,7 @@ UniValue scantxoutset(const JSONRPCRequest& request) " \"txid\" : \"transactionid\", (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" @@ -2101,6 +2160,7 @@ UniValue scantxoutset(const JSONRPCRequest& request) throw JSONRPCError(RPC_INVALID_PARAMETER, "Scan already in progress, use action \"abort\" or \"status\""); } std::set<CScript> needles; + std::map<CScript, std::string> descriptors; CAmount total_in = 0; // loop through the scan objects @@ -2133,7 +2193,11 @@ UniValue scantxoutset(const JSONRPCRequest& request) if (!desc->Expand(i, provider, scripts, provider)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Cannot derive script without private keys: '%s'", desc_str)); } - needles.insert(scripts.begin(), scripts.end()); + for (const auto& script : scripts) { + std::string inferred = InferDescriptor(script, provider)->ToString(); + needles.emplace(script); + descriptors.emplace(std::move(script), std::move(inferred)); + } } } @@ -2166,6 +2230,7 @@ UniValue scantxoutset(const JSONRPCRequest& request) unspent.pushKV("txid", outpoint.hash.GetHex()); unspent.pushKV("vout", (int32_t)outpoint.n); unspent.pushKV("scriptPubKey", HexStr(txo.scriptPubKey.begin(), txo.scriptPubKey.end())); + unspent.pushKV("desc", descriptors[txo.scriptPubKey]); unspent.pushKV("amount", ValueFromAmount(txo.nValue)); unspent.pushKV("height", (int32_t)coin.nHeight); @@ -2179,6 +2244,7 @@ UniValue scantxoutset(const JSONRPCRequest& request) return result; } +// clang-format off static const CRPCCommand commands[] = { // category name actor (function) argNames // --------------------- ------------------------ ----------------------- ---------- @@ -2214,6 +2280,7 @@ static const CRPCCommand commands[] = { "hidden", "waitforblockheight", &waitforblockheight, {"height","timeout"} }, { "hidden", "syncwithvalidationinterfacequeue", &syncwithvalidationinterfacequeue, {} }, }; +// clang-format on void RegisterBlockchainRPCCommands(CRPCTable &t) { diff --git a/src/rpc/blockchain.h b/src/rpc/blockchain.h index 544bc62c36..add335eb8a 100644 --- a/src/rpc/blockchain.h +++ b/src/rpc/blockchain.h @@ -16,8 +16,7 @@ class UniValue; static constexpr int NUM_GETBLOCKSTATS_PERCENTILES = 5; /** - * Get the difficulty of the net wrt to the given block index, or the chain tip if - * not provided. + * Get the difficulty of the net wrt to the given block index. * * @return A floating point number that is a multiple of the main net minimum * difficulty (4295032833 hashes). diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index c7f3e38ac0..6f1bfb03d1 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -5,7 +5,7 @@ #include <rpc/client.h> #include <rpc/protocol.h> -#include <util.h> +#include <util/system.h> #include <set> #include <stdint.h> @@ -18,6 +18,7 @@ public: std::string paramName; //!< parameter name }; +// clang-format off /** * Specify a (method, idx, name) here if the argument is a non-string RPC * argument and needs to be converted from JSON. @@ -40,15 +41,10 @@ static const CRPCConvertParam vRPCConvertParams[] = { "settxfee", 0, "amount" }, { "sethdseed", 0, "newkeypool" }, { "getreceivedbyaddress", 1, "minconf" }, - { "getreceivedbyaccount", 1, "minconf" }, { "getreceivedbylabel", 1, "minconf" }, { "listreceivedbyaddress", 0, "minconf" }, { "listreceivedbyaddress", 1, "include_empty" }, { "listreceivedbyaddress", 2, "include_watchonly" }, - { "listreceivedbyaddress", 3, "address_filter" }, - { "listreceivedbyaccount", 0, "minconf" }, - { "listreceivedbyaccount", 1, "include_empty" }, - { "listreceivedbyaccount", 2, "include_watchonly" }, { "listreceivedbylabel", 0, "minconf" }, { "listreceivedbylabel", 1, "include_empty" }, { "listreceivedbylabel", 2, "include_watchonly" }, @@ -59,15 +55,9 @@ static const CRPCConvertParam vRPCConvertParams[] = { "waitforblockheight", 1, "timeout" }, { "waitforblock", 1, "timeout" }, { "waitfornewblock", 0, "timeout" }, - { "move", 2, "amount" }, - { "move", 3, "minconf" }, - { "sendfrom", 2, "amount" }, - { "sendfrom", 3, "minconf" }, { "listtransactions", 1, "count" }, { "listtransactions", 2, "skip" }, { "listtransactions", 3, "include_watchonly" }, - { "listaccounts", 0, "minconf" }, - { "listaccounts", 1, "include_watchonly" }, { "walletpassphrase", 1, "timeout" }, { "getblocktemplate", 0, "template_request" }, { "listsinceblock", 1, "target_confirmations" }, @@ -157,7 +147,6 @@ static const CRPCConvertParam vRPCConvertParams[] = { "logging", 0, "include" }, { "logging", 1, "exclude" }, { "disconnectnode", 1, "nodeid" }, - { "addwitnessaddress", 1, "p2sh" }, // Echo with conversion (For testing only) { "echojson", 0, "arg0" }, { "echojson", 1, "arg1" }, @@ -172,7 +161,10 @@ static const CRPCConvertParam vRPCConvertParams[] = { "rescanblockchain", 0, "start_height"}, { "rescanblockchain", 1, "stop_height"}, { "createwallet", 1, "disable_private_keys"}, + { "getnodeaddresses", 0, "count"}, + { "stop", 0, "wait" }, }; +// clang-format on class CRPCConvertTable { diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 623b0bd86a..2fd6f99be5 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -18,12 +18,14 @@ #include <rpc/blockchain.h> #include <rpc/mining.h> #include <rpc/server.h> +#include <rpc/util.h> #include <shutdown.h> #include <txmempool.h> -#include <util.h> -#include <utilstrencodings.h> +#include <util/strencodings.h> +#include <util/system.h> #include <validation.h> #include <validationinterface.h> +#include <versionbitsinfo.h> #include <warnings.h> #include <memory> @@ -85,13 +87,15 @@ static UniValue getnetworkhashps(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() > 2) throw std::runtime_error( - "getnetworkhashps ( nblocks height )\n" - "\nReturns the estimated network hashes per second based on the last n blocks.\n" - "Pass in [blocks] to override # of blocks, -1 specifies since last difficulty change.\n" - "Pass in [height] to estimate the network speed at the time when a certain block was found.\n" - "\nArguments:\n" - "1. nblocks (numeric, optional, default=120) The number of blocks, or -1 for blocks since last difficulty change.\n" - "2. height (numeric, optional, default=-1) To estimate at the time of the given height.\n" + RPCHelpMan{"getnetworkhashps", + "\nReturns the estimated network hashes per second based on the last n blocks.\n" + "Pass in [blocks] to override # of blocks, -1 specifies since last difficulty change.\n" + "Pass in [height] to estimate the network speed at the time when a certain block was found.\n", + { + {"nblocks", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "120", "The number of blocks, or -1 for blocks since last difficulty change."}, + {"height", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "-1", "To estimate at the time of the given height."}, + }} + .ToString() + "\nResult:\n" "x (numeric) Hashes per second estimated\n" "\nExamples:\n" @@ -155,17 +159,21 @@ static UniValue generatetoaddress(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 2 || request.params.size() > 3) throw std::runtime_error( - "generatetoaddress nblocks address (maxtries)\n" - "\nMine blocks immediately to a specified address (before the RPC call returns)\n" - "\nArguments:\n" - "1. nblocks (numeric, required) How many blocks are generated immediately.\n" - "2. address (string, required) The address to send the newly generated bitcoin to.\n" - "3. maxtries (numeric, optional) How many iterations to try (default = 1000000).\n" + RPCHelpMan{"generatetoaddress", + "\nMine blocks immediately to a specified address (before the RPC call returns)\n", + { + {"nblocks", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "How many blocks are generated immediately."}, + {"address", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The address to send the newly generated bitcoin to."}, + {"maxtries", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "1000000", "How many iterations to try."}, + }} + .ToString() + "\nResult:\n" "[ blockhashes ] (array) hashes of blocks generated\n" "\nExamples:\n" "\nGenerate 11 blocks to myaddress\n" + HelpExampleCli("generatetoaddress", "11 \"myaddress\"") + + "If you are running the bitcoin core wallet, you can get a new address to send the newly generated bitcoin to with:\n" + + HelpExampleCli("getnewaddress", "") ); int nGenerate = request.params[0].get_int(); @@ -189,8 +197,9 @@ static UniValue getmininginfo(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 0) throw std::runtime_error( - "getmininginfo\n" - "\nReturns a json object containing mining-related information." + RPCHelpMan{"getmininginfo", + "\nReturns a json object containing mining-related information.", {}} + .ToString() + "\nResult:\n" "{\n" " \"blocks\": nnn, (numeric) The current block\n" @@ -228,16 +237,18 @@ static UniValue prioritisetransaction(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 3) throw std::runtime_error( - "prioritisetransaction <txid> <dummy value> <fee delta>\n" - "Accepts the transaction into mined blocks at a higher (or lower) priority\n" - "\nArguments:\n" - "1. \"txid\" (string, required) The transaction id.\n" - "2. dummy (numeric, optional) API-Compatibility for previous API. Must be zero or null.\n" - " DEPRECATED. For forward compatibility use named arguments and omit this parameter.\n" - "3. fee_delta (numeric, required) The fee value (in satoshis) to add (or subtract, if negative).\n" + RPCHelpMan{"prioritisetransaction", + "Accepts the transaction into mined blocks at a higher (or lower) priority\n", + { + {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The transaction id."}, + {"dummy", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "null", "API-Compatibility for previous API. Must be zero or null.\n" + " DEPRECATED. For forward compatibility use named arguments and omit this parameter."}, + {"fee_delta", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "The fee value (in satoshis) to add (or subtract, if negative).\n" " Note, that this value is not a fee rate. It is a value to modify absolute fee of the TX.\n" " The fee is not actually paid, only the algorithm for selecting transactions into a block\n" - " considers the transaction as it would have paid a higher (or lower) fee.\n" + " considers the transaction as it would have paid a higher (or lower) fee."}, + }} + .ToString() + "\nResult:\n" "true (boolean) Returns true\n" "\nExamples:\n" @@ -247,7 +258,7 @@ static UniValue prioritisetransaction(const JSONRPCRequest& request) LOCK(cs_main); - uint256 hash = ParseHashStr(request.params[0].get_str(), "txid"); + uint256 hash(ParseHashV(request.params[0], "txid")); CAmount nAmount = request.params[2].get_int64(); if (!(request.params[1].isNull() || request.params[1].get_real() == 0)) { @@ -291,30 +302,32 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() > 1) throw std::runtime_error( - "getblocktemplate ( TemplateRequest )\n" - "\nIf the request parameters include a 'mode' key, that is used to explicitly select between the default 'template' request or a 'proposal'.\n" - "It returns data needed to construct a block to work on.\n" - "For full specification, see BIPs 22, 23, 9, and 145:\n" - " https://github.com/bitcoin/bips/blob/master/bip-0022.mediawiki\n" - " https://github.com/bitcoin/bips/blob/master/bip-0023.mediawiki\n" - " https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki#getblocktemplate_changes\n" - " https://github.com/bitcoin/bips/blob/master/bip-0145.mediawiki\n" - - "\nArguments:\n" - "1. template_request (json object, optional) A json object in the following spec\n" - " {\n" - " \"mode\":\"template\" (string, optional) This must be set to \"template\", \"proposal\" (see BIP 23), or omitted\n" - " \"capabilities\":[ (array, optional) A list of strings\n" - " \"support\" (string) client side supported feature, 'longpoll', 'coinbasetxn', 'coinbasevalue', 'proposal', 'serverlist', 'workid'\n" - " ,...\n" - " ],\n" - " \"rules\":[ (array, optional) A list of strings\n" - " \"support\" (string) client side supported softfork deployment\n" - " ,...\n" - " ]\n" - " }\n" - "\n" - + RPCHelpMan{"getblocktemplate", + "\nIf the request parameters include a 'mode' key, that is used to explicitly select between the default 'template' request or a 'proposal'.\n" + "It returns data needed to construct a block to work on.\n" + "For full specification, see BIPs 22, 23, 9, and 145:\n" + " https://github.com/bitcoin/bips/blob/master/bip-0022.mediawiki\n" + " https://github.com/bitcoin/bips/blob/master/bip-0023.mediawiki\n" + " https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki#getblocktemplate_changes\n" + " https://github.com/bitcoin/bips/blob/master/bip-0145.mediawiki\n", + { + {"template_request", RPCArg::Type::OBJ, /* opt */ false, /* default_val */ "", "A json object in the following spec", + { + {"mode", RPCArg::Type::STR, /* opt */ true, /* default_val */ "", "This must be set to \"template\", \"proposal\" (see BIP 23), or omitted"}, + {"capabilities", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "", "A list of strings", + { + {"support", RPCArg::Type::STR, /* opt */ true, /* default_val */ "", "client side supported feature, 'longpoll', 'coinbasetxn', 'coinbasevalue', 'proposal', 'serverlist', 'workid'"}, + }, + }, + {"rules", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "A list of strings", + { + {"support", RPCArg::Type::STR, /* opt */ true, /* default_val */ "", "client side supported softfork deployment"}, + }, + }, + }, + "\"template_request\""}, + }} + .ToString() + "\nResult:\n" "{\n" " \"version\" : n, (numeric) The preferred block version\n" @@ -361,8 +374,8 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) "}\n" "\nExamples:\n" - + HelpExampleCli("getblocktemplate", "") - + HelpExampleRpc("getblocktemplate", "") + + HelpExampleCli("getblocktemplate", "{\"rules\": [\"segwit\"]}") + + HelpExampleRpc("getblocktemplate", "{\"rules\": [\"segwit\"]}") ); LOCK(cs_main); @@ -455,7 +468,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) // Format: <hashBestChain><nTransactionsUpdatedLast> std::string lpstr = lpval.get_str(); - hashWatchedChain.SetHex(lpstr.substr(0, 64)); + hashWatchedChain = ParseHashV(lpstr.substr(0, 64), "longpollid"); nTransactionsUpdatedLastLP = atoi64(lpstr.substr(64)); } else @@ -470,7 +483,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) { checktxtime = std::chrono::steady_clock::now() + std::chrono::minutes(1); - WaitableLock lock(g_best_block_mutex); + WAIT_LOCK(g_best_block_mutex, lock); while (g_best_block == hashWatchedChain && IsRPCRunning()) { if (g_best_block_cv.wait_until(lock, checktxtime) == std::cv_status::timeout) @@ -490,21 +503,17 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) } const struct VBDeploymentInfo& segwit_info = VersionBitsDeploymentInfo[Consensus::DEPLOYMENT_SEGWIT]; - // If the caller is indicating segwit support, then allow CreateNewBlock() - // to select witness transactions, after segwit activates (otherwise - // don't). - bool fSupportsSegwit = setClientRules.find(segwit_info.name) != setClientRules.end(); + // GBT must be called with 'segwit' set in the rules + if (setClientRules.count(segwit_info.name) != 1) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "getblocktemplate must be called with the segwit rule set (call with {\"rules\": [\"segwit\"]})"); + } // Update block static CBlockIndex* pindexPrev; static int64_t nStart; static std::unique_ptr<CBlockTemplate> pblocktemplate; - // Cache whether the last invocation was with segwit support, to avoid returning - // a segwit-block to a non-segwit caller. - static bool fLastTemplateSupportsSegwit = true; if (pindexPrev != chainActive.Tip() || - (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 5) || - fLastTemplateSupportsSegwit != fSupportsSegwit) + (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 5)) { // Clear pindexPrev so future calls make a new block, despite any failures from here on pindexPrev = nullptr; @@ -513,11 +522,10 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) nTransactionsUpdatedLast = mempool.GetTransactionsUpdated(); CBlockIndex* pindexPrevNew = chainActive.Tip(); nStart = GetTime(); - fLastTemplateSupportsSegwit = fSupportsSegwit; // Create new block CScript scriptDummy = CScript() << OP_TRUE; - pblocktemplate = BlockAssembler(Params()).CreateNewBlock(scriptDummy, fSupportsSegwit); + pblocktemplate = BlockAssembler(Params()).CreateNewBlock(scriptDummy); if (!pblocktemplate) throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory"); @@ -669,7 +677,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) result.pushKV("bits", strprintf("%08x", pblock->nBits)); result.pushKV("height", (int64_t)(pindexPrev->nHeight+1)); - if (!pblocktemplate->vchCoinbaseCommitment.empty() && fSupportsSegwit) { + if (!pblocktemplate->vchCoinbaseCommitment.empty()) { result.pushKV("default_witness_commitment", HexStr(pblocktemplate->vchCoinbaseCommitment.begin(), pblocktemplate->vchCoinbaseCommitment.end())); } @@ -699,13 +707,14 @@ static UniValue submitblock(const JSONRPCRequest& request) // We allow 2 arguments for compliance with BIP22. Argument 2 is ignored. if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { throw std::runtime_error( - "submitblock \"hexdata\" ( \"dummy\" )\n" - "\nAttempts to submit new block to network.\n" - "See https://en.bitcoin.it/wiki/BIP_0022 for full specification.\n" - - "\nArguments\n" - "1. \"hexdata\" (string, required) the hex-encoded block data to submit\n" - "2. \"dummy\" (optional) dummy value, for compatibility with BIP22. This value is ignored.\n" + RPCHelpMan{"submitblock", + "\nAttempts to submit new block to network.\n" + "See https://en.bitcoin.it/wiki/BIP_0022 for full specification.\n", + { + {"hexdata", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "the hex-encoded block data to submit"}, + {"dummy", RPCArg::Type::STR, /* opt */ true, /* default_val */ "ignored", "dummy value, for compatibility with BIP22. This value is ignored."}, + }} + .ToString() + "\nResult:\n" "\nExamples:\n" + HelpExampleCli("submitblock", "\"mydata\"") @@ -750,11 +759,7 @@ static UniValue submitblock(const JSONRPCRequest& request) RegisterValidationInterface(&sc); bool accepted = ProcessNewBlock(Params(), blockptr, /* fForceProcessing */ true, /* fNewBlock */ &new_block); UnregisterValidationInterface(&sc); - if (!new_block) { - if (!accepted) { - // TODO Maybe pass down fNewBlock to AcceptBlockHeader, so it is properly set to true in this case? - return "invalid"; - } + if (!new_block && accepted) { return "duplicate"; } if (!sc.found) { @@ -767,11 +772,13 @@ static UniValue submitheader(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( - "submitheader \"hexdata\"\n" - "\nDecode the given hexdata as a header and submit it as a candidate chain tip if valid." - "\nThrows when the header is invalid.\n" - "\nArguments\n" - "1. \"hexdata\" (string, required) the hex-encoded block header data\n" + RPCHelpMan{"submitheader", + "\nDecode the given hexdata as a header and submit it as a candidate chain tip if valid." + "\nThrows when the header is invalid.\n", + { + {"hexdata", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "the hex-encoded block header data"}, + }} + .ToString() + "\nResult:\n" "None" "\nExamples:\n" + @@ -799,32 +806,28 @@ static UniValue submitheader(const JSONRPCRequest& request) throw JSONRPCError(RPC_VERIFY_ERROR, state.GetRejectReason()); } -static UniValue estimatefee(const JSONRPCRequest& request) -{ - throw JSONRPCError(RPC_METHOD_DEPRECATED, "estimatefee was removed in v0.17.\n" - "Clients should use estimatesmartfee."); -} - static UniValue estimatesmartfee(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) throw std::runtime_error( - "estimatesmartfee conf_target (\"estimate_mode\")\n" - "\nEstimates the approximate fee per kilobyte needed for a transaction to begin\n" - "confirmation within conf_target blocks if possible and return the number of blocks\n" - "for which the estimate is valid. Uses virtual transaction size as defined\n" - "in BIP 141 (witness data is discounted).\n" - "\nArguments:\n" - "1. conf_target (numeric) Confirmation target in blocks (1 - 1008)\n" - "2. \"estimate_mode\" (string, optional, default=CONSERVATIVE) The fee estimate mode.\n" + RPCHelpMan{"estimatesmartfee", + "\nEstimates the approximate fee per kilobyte needed for a transaction to begin\n" + "confirmation within conf_target blocks if possible and return the number of blocks\n" + "for which the estimate is valid. Uses virtual transaction size as defined\n" + "in BIP 141 (witness data is discounted).\n", + { + {"conf_target", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "Confirmation target in blocks (1 - 1008)"}, + {"estimate_mode", RPCArg::Type::STR, /* opt */ true, /* default_val */ "CONSERVATIVE", "The fee estimate mode.\n" " Whether to return a more conservative estimate which also satisfies\n" " a longer history. A conservative estimate potentially returns a\n" " higher feerate and is more likely to be sufficient for the desired\n" " target, but is not as responsive to short term drops in the\n" " prevailing fee market. Must be one of:\n" - " \"UNSET\" (defaults to CONSERVATIVE)\n" + " \"UNSET\"\n" " \"ECONOMICAL\"\n" - " \"CONSERVATIVE\"\n" + " \"CONSERVATIVE\""}, + }} + .ToString() + "\nResult:\n" "{\n" " \"feerate\" : x.x, (numeric, optional) estimate fee rate in " + CURRENCY_UNIT + "/kB\n" @@ -870,19 +873,21 @@ static UniValue estimaterawfee(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) throw std::runtime_error( - "estimaterawfee conf_target (threshold)\n" - "\nWARNING: This interface is unstable and may disappear or change!\n" - "\nWARNING: This is an advanced API call that is tightly coupled to the specific\n" - " implementation of fee estimation. The parameters it can be called with\n" - " and the results it returns will change if the internal implementation changes.\n" - "\nEstimates the approximate fee per kilobyte needed for a transaction to begin\n" - "confirmation within conf_target blocks if possible. Uses virtual transaction size as\n" - "defined in BIP 141 (witness data is discounted).\n" - "\nArguments:\n" - "1. conf_target (numeric) Confirmation target in blocks (1 - 1008)\n" - "2. threshold (numeric, optional) The proportion of transactions in a given feerate range that must have been\n" + RPCHelpMan{"estimaterawfee", + "\nWARNING: This interface is unstable and may disappear or change!\n" + "\nWARNING: This is an advanced API call that is tightly coupled to the specific\n" + " implementation of fee estimation. The parameters it can be called with\n" + " and the results it returns will change if the internal implementation changes.\n" + "\nEstimates the approximate fee per kilobyte needed for a transaction to begin\n" + "confirmation within conf_target blocks if possible. Uses virtual transaction size as\n" + "defined in BIP 141 (witness data is discounted).\n", + { + {"conf_target", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "Confirmation target in blocks (1 - 1008)"}, + {"threshold", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "0.95", "The proportion of transactions in a given feerate range that must have been\n" " confirmed within conf_target in order to consider those feerates as high enough and proceed to check\n" - " lower buckets. Default: 0.95\n" + " lower buckets."}, + }} + .ToString() + "\nResult:\n" "{\n" " \"short\" : { (json object, optional) estimate for short time horizon\n" @@ -922,7 +927,7 @@ static UniValue estimaterawfee(const JSONRPCRequest& request) UniValue result(UniValue::VOBJ); - for (FeeEstimateHorizon horizon : {FeeEstimateHorizon::SHORT_HALFLIFE, FeeEstimateHorizon::MED_HALFLIFE, FeeEstimateHorizon::LONG_HALFLIFE}) { + for (const FeeEstimateHorizon horizon : {FeeEstimateHorizon::SHORT_HALFLIFE, FeeEstimateHorizon::MED_HALFLIFE, FeeEstimateHorizon::LONG_HALFLIFE}) { CFeeRate feeRate; EstimationResult buckets; @@ -968,6 +973,7 @@ static UniValue estimaterawfee(const JSONRPCRequest& request) return result; } +// clang-format off static const CRPCCommand commands[] = { // category name actor (function) argNames // --------------------- ------------------------ ----------------------- ---------- @@ -981,11 +987,11 @@ static const CRPCCommand commands[] = { "generating", "generatetoaddress", &generatetoaddress, {"nblocks","address","maxtries"} }, - { "hidden", "estimatefee", &estimatefee, {} }, { "util", "estimatesmartfee", &estimatesmartfee, {"conf_target", "estimate_mode"} }, { "hidden", "estimaterawfee", &estimaterawfee, {"conf_target", "threshold"} }, }; +// clang-format on void RegisterMiningRPCCommands(CRPCTable &t) { diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 40418545c1..18c867bff3 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -17,13 +17,8 @@ #include <rpc/server.h> #include <rpc/util.h> #include <timedata.h> -#include <util.h> -#include <utilstrencodings.h> -#ifdef ENABLE_WALLET -#include <wallet/rpcwallet.h> -#include <wallet/wallet.h> -#include <wallet/walletdb.h> -#endif +#include <util/system.h> +#include <util/strencodings.h> #include <warnings.h> #include <stdint.h> @@ -37,19 +32,21 @@ static UniValue validateaddress(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 1) throw std::runtime_error( - "validateaddress \"address\"\n" - "\nReturn information about the given bitcoin address.\n" - "DEPRECATION WARNING: Parts of this command have been deprecated and moved to getaddressinfo. Clients must\n" - "transition to using getaddressinfo to access this information before upgrading to v0.18. The following deprecated\n" - "fields have moved to getaddressinfo and will only be shown here with -deprecatedrpc=validateaddress: ismine, iswatchonly,\n" - "script, hex, pubkeys, sigsrequired, pubkey, addresses, embedded, iscompressed, account, timestamp, hdkeypath, kdmasterkeyid.\n" - "\nArguments:\n" - "1. \"address\" (string, required) The bitcoin address to validate\n" + RPCHelpMan{"validateaddress", + "\nReturn information about the given bitcoin address.\n" + "DEPRECATION WARNING: Parts of this command have been deprecated and moved to getaddressinfo. Clients must\n" + "transition to using getaddressinfo to access this information before upgrading to v0.18. The following deprecated\n" + "fields have moved to getaddressinfo and will only be shown here with -deprecatedrpc=validateaddress: ismine, iswatchonly,\n" + "script, hex, pubkeys, sigsrequired, pubkey, addresses, embedded, iscompressed, account, timestamp, hdkeypath, kdmasterkeyid.\n", + { + {"address", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The bitcoin address to validate"}, + }} + .ToString() + "\nResult:\n" "{\n" " \"isvalid\" : true|false, (boolean) If the address is valid or not. If not, this is the only property returned.\n" " \"address\" : \"address\", (string) The bitcoin address validated\n" - " \"scriptPubKey\" : \"hex\", (string) The hex encoded scriptPubKey generated by the address\n" + " \"scriptPubKey\" : \"hex\", (string) The hex-encoded scriptPubKey generated by the address\n" " \"isscript\" : true|false, (boolean) If the key is a script\n" " \"iswitness\" : true|false, (boolean) If the address is a witness address\n" " \"witness_version\" : version (numeric, optional) The version number of the witness program\n" @@ -67,45 +64,35 @@ static UniValue validateaddress(const JSONRPCRequest& request) ret.pushKV("isvalid", isValid); if (isValid) { + std::string currentAddress = EncodeDestination(dest); + ret.pushKV("address", currentAddress); -#ifdef ENABLE_WALLET - if (HasWallets() && IsDeprecatedRPCEnabled("validateaddress")) { - ret.pushKVs(getaddressinfo(request)); - } -#endif - if (ret["address"].isNull()) { - std::string currentAddress = EncodeDestination(dest); - ret.pushKV("address", currentAddress); - - CScript scriptPubKey = GetScriptForDestination(dest); - ret.pushKV("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end())); + CScript scriptPubKey = GetScriptForDestination(dest); + ret.pushKV("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end())); - UniValue detail = DescribeAddress(dest); - ret.pushKVs(detail); - } + UniValue detail = DescribeAddress(dest); + ret.pushKVs(detail); } return ret; } -// Needed even with !ENABLE_WALLET, to pass (ignored) pointers around -class CWallet; - static UniValue createmultisig(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 2 || request.params.size() > 3) { - std::string msg = "createmultisig nrequired [\"key\",...] ( \"address_type\" )\n" - "\nCreates a multi-signature address with n signature of m keys required.\n" - "It returns a json object with the address and redeemScript.\n" - "\nArguments:\n" - "1. nrequired (numeric, required) The number of required signatures out of the n keys.\n" - "2. \"keys\" (string, required) A json array of hex-encoded public keys\n" - " [\n" - " \"key\" (string) The hex-encoded public key\n" - " ,...\n" - " ]\n" - "3. \"address_type\" (string, optional) The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\". Default is legacy.\n" - + std::string msg = + RPCHelpMan{"createmultisig", + "\nCreates a multi-signature address with n signature of m keys required.\n" + "It returns a json object with the address and redeemScript.\n", + { + {"nrequired", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "The number of required signatures out of the n keys."}, + {"keys", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "A json array of hex-encoded public keys.", + { + {"key", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The hex-encoded public key"}, + }}, + {"address_type", RPCArg::Type::STR, /* opt */ true, /* default_val */ "legacy", "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."}, + }} + .ToString() + "\nResult:\n" "{\n" " \"address\":\"multisigaddress\", (string) The value of the new multisig address.\n" @@ -115,7 +102,7 @@ static UniValue createmultisig(const JSONRPCRequest& request) "\nExamples:\n" "\nCreate a multisig address from 2 public keys\n" + HelpExampleCli("createmultisig", "2 \"[\\\"03789ed0bb717d88f7d321a368d905e7430207ebbd82bd342cf11ae157a7ace5fd\\\",\\\"03dbc6764b8884a92e871274b87583e6d5c2a58819473e17e107ef3f6aa5a61626\\\"]\"") + - "\nAs a json rpc call\n" + "\nAs a JSON-RPC call\n" + HelpExampleRpc("createmultisig", "2, \"[\\\"03789ed0bb717d88f7d321a368d905e7430207ebbd82bd342cf11ae157a7ace5fd\\\",\\\"03dbc6764b8884a92e871274b87583e6d5c2a58819473e17e107ef3f6aa5a61626\\\"]\"") ; throw std::runtime_error(msg); @@ -130,8 +117,7 @@ static UniValue createmultisig(const JSONRPCRequest& request) if (IsHex(keys[i].get_str()) && (keys[i].get_str().length() == 66 || keys[i].get_str().length() == 130)) { pubkeys.push_back(HexToPubKey(keys[i].get_str())); } else { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Invalid public key: %s\nNote that from v0.16, createmultisig no longer accepts addresses." - " Users must use addmultisigaddress to create multisig addresses with addresses known to the wallet.", keys[i].get_str())); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Invalid public key: %s\n.", keys[i].get_str())); } } @@ -159,12 +145,14 @@ static UniValue verifymessage(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 3) throw std::runtime_error( - "verifymessage \"address\" \"signature\" \"message\"\n" - "\nVerify a signed message\n" - "\nArguments:\n" - "1. \"address\" (string, required) The bitcoin address to use for the signature.\n" - "2. \"signature\" (string, required) The signature provided by the signer in base 64 encoding (see signmessage).\n" - "3. \"message\" (string, required) The message that was signed.\n" + RPCHelpMan{"verifymessage", + "\nVerify a signed message\n", + { + {"address", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The bitcoin address to use for the signature."}, + {"signature", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The signature provided by the signer in base 64 encoding (see signmessage)."}, + {"message", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The message that was signed."}, + }} + .ToString() + "\nResult:\n" "true|false (boolean) If the signature is verified or not.\n" "\nExamples:\n" @@ -174,7 +162,7 @@ static UniValue verifymessage(const JSONRPCRequest& request) + HelpExampleCli("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"my message\"") + "\nVerify the signature\n" + HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"signature\" \"my message\"") + - "\nAs json rpc\n" + "\nAs a JSON-RPC call\n" + HelpExampleRpc("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"signature\", \"my message\"") ); @@ -215,11 +203,13 @@ static UniValue signmessagewithprivkey(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 2) throw std::runtime_error( - "signmessagewithprivkey \"privkey\" \"message\"\n" - "\nSign a message with the private key of an address\n" - "\nArguments:\n" - "1. \"privkey\" (string, required) The private key to sign the message with.\n" - "2. \"message\" (string, required) The message to create a signature of.\n" + RPCHelpMan{"signmessagewithprivkey", + "\nSign a message with the private key of an address\n", + { + {"privkey", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The private key to sign the message with."}, + {"message", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The message to create a signature of."}, + }} + .ToString() + "\nResult:\n" "\"signature\" (string) The signature of the message encoded in base 64\n" "\nExamples:\n" @@ -227,7 +217,7 @@ static UniValue signmessagewithprivkey(const JSONRPCRequest& request) + HelpExampleCli("signmessagewithprivkey", "\"privkey\" \"my message\"") + "\nVerify the signature\n" + HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"signature\" \"my message\"") + - "\nAs json rpc\n" + "\nAs a JSON-RPC call\n" + HelpExampleRpc("signmessagewithprivkey", "\"privkey\", \"my message\"") ); @@ -254,11 +244,13 @@ static UniValue setmocktime(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 1) throw std::runtime_error( - "setmocktime timestamp\n" - "\nSet the local time to given timestamp (-regtest only)\n" - "\nArguments:\n" - "1. timestamp (integer, required) Unix seconds-since-epoch timestamp\n" - " Pass 0 to go back to using the system time." + RPCHelpMan{"setmocktime", + "\nSet the local time to given timestamp (-regtest only)\n", + { + {"timestamp", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "Unix seconds-since-epoch timestamp\n" + " Pass 0 to go back to using the system time."}, + }} + .ToString() ); if (!Params().MineBlocksOnDemand()) @@ -316,12 +308,14 @@ static UniValue getmemoryinfo(const JSONRPCRequest& request) */ if (request.fHelp || request.params.size() > 1) throw std::runtime_error( - "getmemoryinfo (\"mode\")\n" - "Returns an object containing information about memory usage.\n" - "Arguments:\n" - "1. \"mode\" determines what kind of information is returned. This argument is optional, the default mode is \"stats\".\n" + RPCHelpMan{"getmemoryinfo", + "Returns an object containing information about memory usage.\n", + { + {"mode", RPCArg::Type::STR, /* opt */ true, /* default_val */ "\"stats\"", "determines what kind of information is returned.\n" " - \"stats\" returns general statistics about memory usage in the daemon.\n" - " - \"mallocinfo\" returns an XML string describing low-level heap state (only available if compiled with glibc 2.10+).\n" + " - \"mallocinfo\" returns an XML string describing low-level heap state (only available if compiled with glibc 2.10+)."}, + }} + .ToString() + "\nResult (mode \"stats\"):\n" "{\n" " \"locked\": { (json object) Information about locked memory manager\n" @@ -378,7 +372,7 @@ UniValue logging(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() > 2) { throw std::runtime_error( - "logging ( <include> <exclude> )\n" + RPCHelpMan{"logging", "Gets and sets the logging configuration.\n" "When called without an argument, returns the list of categories with status that are currently being debug logged or not.\n" "When called with arguments, adds or removes categories from debug logging and return the lists above.\n" @@ -388,17 +382,18 @@ UniValue logging(const JSONRPCRequest& request) "In addition, the following are available as category names with special meanings:\n" " - \"all\", \"1\" : represent all logging categories.\n" " - \"none\", \"0\" : even if other logging categories are specified, ignore all of them.\n" - "\nArguments:\n" - "1. \"include\" (array of strings, optional) A json array of categories to add debug logging\n" - " [\n" - " \"category\" (string) the valid logging category\n" - " ,...\n" - " ]\n" - "2. \"exclude\" (array of strings, optional) A json array of categories to remove debug logging\n" - " [\n" - " \"category\" (string) the valid logging category\n" - " ,...\n" - " ]\n" + , + { + {"include", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "null", "A json array of categories to add debug logging", + { + {"include_category", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "the valid logging category"}, + }}, + {"exclude", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "null", "A json array of categories to remove debug logging", + { + {"exclude_category", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "the valid logging category"}, + }}, + }} + .ToString() + "\nResult:\n" "{ (json object where keys are the logging categories, and values indicates its status\n" " \"category\": 0|1, (numeric) if being debug logged or not. 0:inactive, 1:active\n" @@ -447,22 +442,25 @@ static UniValue echo(const JSONRPCRequest& request) { if (request.fHelp) throw std::runtime_error( - "echo|echojson \"message\" ...\n" - "\nSimply echo back the input arguments. This command is for testing.\n" - "\nThe difference between echo and echojson is that echojson has argument conversion enabled in the client-side table in" - "bitcoin-cli and the GUI. There is no server-side difference." - ); + RPCHelpMan{"echo|echojson ...", + "\nSimply echo back the input arguments. This command is for testing.\n" + "\nThe difference between echo and echojson is that echojson has argument conversion enabled in the client-side table in " + "bitcoin-cli and the GUI. There is no server-side difference.", + {}} + .ToString() + + ""); return request.params; } +// clang-format off static const CRPCCommand commands[] = { // category name actor (function) argNames // --------------------- ------------------------ ----------------------- ---------- { "control", "getmemoryinfo", &getmemoryinfo, {"mode"} }, { "control", "logging", &logging, {"include", "exclude"}}, - { "util", "validateaddress", &validateaddress, {"address"} }, /* uses wallet if enabled */ - { "util", "createmultisig", &createmultisig, {"nrequired","keys"} }, + { "util", "validateaddress", &validateaddress, {"address"} }, + { "util", "createmultisig", &createmultisig, {"nrequired","keys","address_type"} }, { "util", "verifymessage", &verifymessage, {"address","signature","message"} }, { "util", "signmessagewithprivkey", &signmessagewithprivkey, {"privkey","message"} }, @@ -471,6 +469,7 @@ static const CRPCCommand commands[] = { "hidden", "echo", &echo, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}}, { "hidden", "echojson", &echo, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}}, }; +// clang-format on void RegisterMiscRPCCommands(CRPCTable &t) { diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 610a1fee6f..4d6b260cc7 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -7,17 +7,18 @@ #include <chainparams.h> #include <clientversion.h> #include <core_io.h> -#include <validation.h> #include <net.h> #include <net_processing.h> #include <netbase.h> #include <policy/policy.h> #include <rpc/protocol.h> +#include <rpc/util.h> #include <sync.h> #include <timedata.h> #include <ui_interface.h> -#include <util.h> -#include <utilstrencodings.h> +#include <util/strencodings.h> +#include <util/system.h> +#include <validation.h> #include <version.h> #include <warnings.h> @@ -27,8 +28,9 @@ static UniValue getconnectioncount(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 0) throw std::runtime_error( - "getconnectioncount\n" - "\nReturns the number of connections to other nodes.\n" + RPCHelpMan{"getconnectioncount", + "\nReturns the number of connections to other nodes.\n", {}} + .ToString() + "\nResult:\n" "n (numeric) The connection count\n" "\nExamples:\n" @@ -46,10 +48,12 @@ static UniValue ping(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 0) throw std::runtime_error( - "ping\n" - "\nRequests that a ping be sent to all other nodes, to measure ping time.\n" - "Results provided in getpeerinfo, pingtime and pingwait fields are decimal seconds.\n" - "Ping command is handled in queue with all other commands, so it measures processing backlog, not just network ping.\n" + RPCHelpMan{"ping", + "\nRequests that a ping be sent to all other nodes, to measure ping time.\n" + "Results provided in getpeerinfo, pingtime and pingwait fields are decimal seconds.\n" + "Ping command is handled in queue with all other commands, so it measures processing backlog, not just network ping.\n", + {}} + .ToString() + "\nExamples:\n" + HelpExampleCli("ping", "") + HelpExampleRpc("ping", "") @@ -69,8 +73,9 @@ static UniValue getpeerinfo(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 0) throw std::runtime_error( - "getpeerinfo\n" - "\nReturns data about each connected network node as a json array of objects.\n" + RPCHelpMan{"getpeerinfo", + "\nReturns data about each connected network node as a json array of objects.\n", {}} + .ToString() + "\nResult:\n" "[\n" " {\n" @@ -102,6 +107,7 @@ static UniValue getpeerinfo(const JSONRPCRequest& request) " ...\n" " ],\n" " \"whitelisted\": true|false, (boolean) Whether the peer is whitelisted\n" + " \"minfeefilter\": n, (numeric) The minimum fee rate for transactions this peer accepts\n" " \"bytessent_per_msg\": {\n" " \"addr\": n, (numeric) The total bytes sent aggregated by message type\n" " ...\n" @@ -163,12 +169,13 @@ static UniValue getpeerinfo(const JSONRPCRequest& request) obj.pushKV("synced_headers", statestats.nSyncHeight); obj.pushKV("synced_blocks", statestats.nCommonHeight); UniValue heights(UniValue::VARR); - for (int height : statestats.vHeightInFlight) { + for (const int height : statestats.vHeightInFlight) { heights.push_back(height); } obj.pushKV("inflight", heights); } obj.pushKV("whitelisted", stats.fWhitelisted); + obj.pushKV("minfeefilter", ValueFromAmount(stats.minFeeFilter)); UniValue sendPerMsgCmd(UniValue::VOBJ); for (const mapMsgCmdSize::value_type &i : stats.mapSendBytesPerMsgCmd) { @@ -198,14 +205,16 @@ static UniValue addnode(const JSONRPCRequest& request) if (request.fHelp || request.params.size() != 2 || (strCommand != "onetry" && strCommand != "add" && strCommand != "remove")) throw std::runtime_error( - "addnode \"node\" \"add|remove|onetry\"\n" - "\nAttempts to add or remove a node from the addnode list.\n" - "Or try a connection to a node once.\n" - "Nodes added using addnode (or -connect) are protected from DoS disconnection and are not required to be\n" - "full nodes/support SegWit as other outbound peers are (though such peers will not be synced from).\n" - "\nArguments:\n" - "1. \"node\" (string, required) The node (see getpeerinfo for nodes)\n" - "2. \"command\" (string, required) 'add' to add a node to the list, 'remove' to remove a node from the list, 'onetry' to try a connection to the node once\n" + RPCHelpMan{"addnode", + "\nAttempts to add or remove a node from the addnode list.\n" + "Or try a connection to a node once.\n" + "Nodes added using addnode (or -connect) are protected from DoS disconnection and are not required to be\n" + "full nodes/support SegWit as other outbound peers are (though such peers will not be synced from).\n", + { + {"node", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The node (see getpeerinfo for nodes)"}, + {"command", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "'add' to add a node to the list, 'remove' to remove a node from the list, 'onetry' to try a connection to the node once"}, + }} + .ToString() + "\nExamples:\n" + HelpExampleCli("addnode", "\"192.168.0.6:8333\" \"onetry\"") + HelpExampleRpc("addnode", "\"192.168.0.6:8333\", \"onetry\"") @@ -241,13 +250,15 @@ static UniValue disconnectnode(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() == 0 || request.params.size() >= 3) throw std::runtime_error( - "disconnectnode \"[address]\" [nodeid]\n" - "\nImmediately disconnects from the specified peer node.\n" - "\nStrictly one out of 'address' and 'nodeid' can be provided to identify the node.\n" - "\nTo disconnect by nodeid, either set 'address' to the empty string, or call using the named 'nodeid' argument only.\n" - "\nArguments:\n" - "1. \"address\" (string, optional) The IP address/port of the node\n" - "2. \"nodeid\" (number, optional) The node ID (see getpeerinfo for node IDs)\n" + RPCHelpMan{"disconnectnode", + "\nImmediately disconnects from the specified peer node.\n" + "\nStrictly one out of 'address' and 'nodeid' can be provided to identify the node.\n" + "\nTo disconnect by nodeid, either set 'address' to the empty string, or call using the named 'nodeid' argument only.\n", + { + {"address", RPCArg::Type::STR, /* opt */ true, /* default_val */ "fallback to nodeid", "The IP address/port of the node"}, + {"nodeid", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "fallback to address", "The node ID (see getpeerinfo for node IDs)"}, + }} + .ToString() + "\nExamples:\n" + HelpExampleCli("disconnectnode", "\"192.168.0.6:8333\"") + HelpExampleCli("disconnectnode", "\"\" 1") @@ -284,11 +295,13 @@ static UniValue getaddednodeinfo(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() > 1) throw std::runtime_error( - "getaddednodeinfo ( \"node\" )\n" - "\nReturns information about the given added node, or all added nodes\n" - "(note that onetry addnodes are not listed here)\n" - "\nArguments:\n" - "1. \"node\" (string, optional) If provided, return information about this specific node, otherwise all nodes are returned.\n" + RPCHelpMan{"getaddednodeinfo", + "\nReturns information about the given added node, or all added nodes\n" + "(note that onetry addnodes are not listed here)\n", + { + {"node", RPCArg::Type::STR, /* opt */ true, /* default_val */ "all nodes", "If provided, return information about this specific node, otherwise all nodes are returned."}, + }} + .ToString() + "\nResult:\n" "[\n" " {\n" @@ -351,9 +364,11 @@ static UniValue getnettotals(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() > 0) throw std::runtime_error( - "getnettotals\n" - "\nReturns information about network traffic, including bytes in, bytes out,\n" - "and current time.\n" + RPCHelpMan{"getnettotals", + "\nReturns information about network traffic, including bytes in, bytes out,\n" + "and current time.\n", + {}} + .ToString() + "\nResult:\n" "{\n" " \"totalbytesrecv\": n, (numeric) Total bytes received\n" @@ -417,8 +432,9 @@ static UniValue getnetworkinfo(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 0) throw std::runtime_error( - "getnetworkinfo\n" - "Returns an object containing various state info regarding P2P networking.\n" + RPCHelpMan{"getnetworkinfo", + "Returns an object containing various state info regarding P2P networking.\n", {}} + .ToString() + "\nResult:\n" "{\n" " \"version\": xxxxx, (numeric) the server version\n" @@ -497,13 +513,15 @@ static UniValue setban(const JSONRPCRequest& request) if (request.fHelp || request.params.size() < 2 || (strCommand != "add" && strCommand != "remove")) throw std::runtime_error( - "setban \"subnet\" \"add|remove\" (bantime) (absolute)\n" - "\nAttempts to add or remove an IP/Subnet from the banned list.\n" - "\nArguments:\n" - "1. \"subnet\" (string, required) The IP/Subnet (see getpeerinfo for nodes IP) with an optional netmask (default is /32 = single IP)\n" - "2. \"command\" (string, required) 'add' to add an IP/Subnet to the list, 'remove' to remove an IP/Subnet from the list\n" - "3. \"bantime\" (numeric, optional) time in seconds how long (or until when if [absolute] is set) the IP is banned (0 or empty means using the default time of 24h which can also be overwritten by the -bantime startup argument)\n" - "4. \"absolute\" (boolean, optional) If set, the bantime must be an absolute timestamp in seconds since epoch (Jan 1 1970 GMT)\n" + RPCHelpMan{"setban", + "\nAttempts to add or remove an IP/Subnet from the banned list.\n", + { + {"subnet", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The IP/Subnet (see getpeerinfo for nodes IP) with an optional netmask (default is /32 = single IP)"}, + {"command", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "'add' to add an IP/Subnet to the list, 'remove' to remove an IP/Subnet from the list"}, + {"bantime", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "0", "time in seconds how long (or until when if [absolute] is set) the IP is banned (0 or empty means using the default time of 24h which can also be overwritten by the -bantime startup argument)"}, + {"absolute", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "If set, the bantime must be an absolute timestamp in seconds since epoch (Jan 1 1970 GMT)"}, + }} + .ToString() + "\nExamples:\n" + HelpExampleCli("setban", "\"192.168.0.6\" \"add\" 86400") + HelpExampleCli("setban", "\"192.168.0.0/24\" \"add\"") @@ -557,8 +575,9 @@ static UniValue listbanned(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 0) throw std::runtime_error( - "listbanned\n" - "\nList all banned IPs/Subnets.\n" + RPCHelpMan{"listbanned", + "\nList all banned IPs/Subnets.\n", {}} + .ToString() + "\nExamples:\n" + HelpExampleCli("listbanned", "") + HelpExampleRpc("listbanned", "") @@ -590,8 +609,9 @@ static UniValue clearbanned(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 0) throw std::runtime_error( - "clearbanned\n" - "\nClear all banned IPs.\n" + RPCHelpMan{"clearbanned", + "\nClear all banned IPs.\n", {}} + .ToString() + "\nExamples:\n" + HelpExampleCli("clearbanned", "") + HelpExampleRpc("clearbanned", "") @@ -608,10 +628,12 @@ static UniValue setnetworkactive(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( - "setnetworkactive true|false\n" - "\nDisable/enable all p2p network activity.\n" - "\nArguments:\n" - "1. \"state\" (boolean, required) true to enable networking, false to disable\n" + RPCHelpMan{"setnetworkactive", + "\nDisable/enable all p2p network activity.\n", + { + {"state", RPCArg::Type::BOOL, /* opt */ false, /* default_val */ "", "true to enable networking, false to disable"}, + }} + .ToString() ); } @@ -624,6 +646,60 @@ static UniValue setnetworkactive(const JSONRPCRequest& request) return g_connman->GetNetworkActive(); } +static UniValue getnodeaddresses(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() > 1) { + throw std::runtime_error( + RPCHelpMan{"getnodeaddresses", + "\nReturn known addresses which can potentially be used to find new nodes in the network\n", + { + {"count", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "1", "How many addresses to return. Limited to the smaller of " + std::to_string(ADDRMAN_GETADDR_MAX) + " or " + std::to_string(ADDRMAN_GETADDR_MAX_PCT) + "% of all known addresses."}, + }} + .ToString() + + "\nResult:\n" + "[\n" + " {\n" + " \"time\": ttt, (numeric) Timestamp in seconds since epoch (Jan 1 1970 GMT) keeping track of when the node was last seen\n" + " \"services\": n, (numeric) The services offered\n" + " \"address\": \"host\", (string) The address of the node\n" + " \"port\": n (numeric) The port of the node\n" + " }\n" + " ,....\n" + "]\n" + "\nExamples:\n" + + HelpExampleCli("getnodeaddresses", "8") + + HelpExampleRpc("getnodeaddresses", "8") + ); + } + if (!g_connman) { + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + } + + int count = 1; + if (!request.params[0].isNull()) { + count = request.params[0].get_int(); + if (count <= 0) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Address count out of range"); + } + } + // returns a shuffled list of CAddress + std::vector<CAddress> vAddr = g_connman->GetAddresses(); + UniValue ret(UniValue::VARR); + + int address_return_count = std::min<int>(count, vAddr.size()); + for (int i = 0; i < address_return_count; ++i) { + UniValue obj(UniValue::VOBJ); + const CAddress& addr = vAddr[i]; + obj.pushKV("time", (int)addr.nTime); + obj.pushKV("services", (uint64_t)addr.nServices); + obj.pushKV("address", addr.ToStringIP()); + obj.pushKV("port", addr.GetPort()); + ret.push_back(obj); + } + return ret; +} + +// clang-format off static const CRPCCommand commands[] = { // category name actor (function) argNames // --------------------- ------------------------ ----------------------- ---------- @@ -639,7 +715,9 @@ static const CRPCCommand commands[] = { "network", "listbanned", &listbanned, {} }, { "network", "clearbanned", &clearbanned, {} }, { "network", "setnetworkactive", &setnetworkactive, {"state"} }, + { "network", "getnodeaddresses", &getnodeaddresses, {"count"} }, }; +// clang-format on void RegisterNetRPCCommands(CRPCTable &t) { diff --git a/src/rpc/protocol.cpp b/src/rpc/protocol.cpp index dab63ab263..23999b305a 100644 --- a/src/rpc/protocol.cpp +++ b/src/rpc/protocol.cpp @@ -7,13 +7,11 @@ #include <random.h> #include <tinyformat.h> -#include <util.h> -#include <utilstrencodings.h> -#include <utiltime.h> +#include <util/system.h> +#include <util/strencodings.h> +#include <util/time.h> #include <version.h> -#include <fstream> - /** * JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility, * but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were @@ -85,9 +83,9 @@ bool GenerateAuthCookie(std::string *cookie_out) /** the umask determines what permissions are used to create this file - * these are set to 077 in init.cpp unless overridden with -sysperms. */ - std::ofstream file; + fsbridge::ofstream file; fs::path filepath_tmp = GetAuthCookieFile(true); - file.open(filepath_tmp.string().c_str()); + file.open(filepath_tmp); if (!file.is_open()) { LogPrintf("Unable to open cookie authentication file %s for writing\n", filepath_tmp.string()); return false; @@ -109,10 +107,10 @@ bool GenerateAuthCookie(std::string *cookie_out) bool GetAuthCookie(std::string *cookie_out) { - std::ifstream file; + fsbridge::ifstream file; std::string cookie; fs::path filepath = GetAuthCookieFile(); - file.open(filepath.string().c_str()); + file.open(filepath); if (!file.is_open()) return false; std::getline(file, cookie); @@ -128,7 +126,7 @@ void DeleteAuthCookie() try { fs::remove(GetAuthCookieFile()); } catch (const fs::filesystem_error& e) { - LogPrintf("%s: Unable to remove random auth cookie file: %s\n", __func__, e.what()); + LogPrintf("%s: Unable to remove random auth cookie file: %s\n", __func__, fsbridge::get_filesystem_error_message(e)); } } diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 23845ff517..bf00870107 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -9,10 +9,9 @@ #include <consensus/validation.h> #include <core_io.h> #include <index/txindex.h> -#include <keystore.h> -#include <validation.h> -#include <validationinterface.h> +#include <init.h> #include <key_io.h> +#include <keystore.h> #include <merkleblock.h> #include <net.h> #include <policy/policy.h> @@ -20,16 +19,16 @@ #include <primitives/transaction.h> #include <rpc/rawtransaction.h> #include <rpc/server.h> +#include <rpc/util.h> #include <script/script.h> #include <script/script_error.h> #include <script/sign.h> #include <script/standard.h> #include <txmempool.h> #include <uint256.h> -#include <utilstrencodings.h> -#ifdef ENABLE_WALLET -#include <wallet/rpcwallet.h> -#endif +#include <util/strencodings.h> +#include <validation.h> +#include <validationinterface.h> #include <future> #include <stdint.h> @@ -67,24 +66,24 @@ static UniValue getrawtransaction(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 3) throw std::runtime_error( - "getrawtransaction \"txid\" ( verbose \"blockhash\" )\n" - - "\nNOTE: By default this function only works for mempool transactions. If the -txindex option is\n" - "enabled, it also works for blockchain transactions. If the block which contains the transaction\n" - "is known, its hash can be provided even for nodes without -txindex. Note that if a blockhash is\n" - "provided, only that block will be searched and if the transaction is in the mempool or other\n" - "blocks, or if this node does not have the given block available, the transaction will not be found.\n" + RPCHelpMan{"getrawtransaction", + "\nNOTE: By default this function only works for mempool transactions. If the -txindex option is\n" + "enabled, it also works for blockchain transactions. If the block which contains the transaction\n" + "is known, its hash can be provided even for nodes without -txindex. Note that if a blockhash is\n" + "provided, only that block will be searched and if the transaction is in the mempool or other\n" + "blocks, or if this node does not have the given block available, the transaction will not be found.\n" "DEPRECATED: for now, it also works for transactions with unspent outputs.\n" "\nReturn the raw transaction data.\n" "\nIf verbose is 'true', returns an Object with information about 'txid'.\n" "If verbose is 'false' or omitted, returns a string that is serialized, hex-encoded data for 'txid'.\n" - - "\nArguments:\n" - "1. \"txid\" (string, required) The transaction id\n" - "2. verbose (bool, optional, default=false) If false, return a string, otherwise return a json object\n" - "3. \"blockhash\" (string, optional) The block in which to look for the transaction\n" - + , + { + {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The transaction id"}, + {"verbose", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "If false, return a string, otherwise return a json object"}, + {"blockhash", RPCArg::Type::STR_HEX, /* opt */ true, /* default_val */ "null", "The block in which to look for the transaction"}, + }} + .ToString() + "\nResult (if verbose is not set or set to false):\n" "\"data\" (string) The serialized, hex-encoded data for 'txid'\n" @@ -131,8 +130,8 @@ static UniValue getrawtransaction(const JSONRPCRequest& request) " ],\n" " \"blockhash\" : \"hash\", (string) the block hash\n" " \"confirmations\" : n, (numeric) The confirmations\n" - " \"time\" : ttt, (numeric) The transaction time in seconds since epoch (Jan 1 1970 GMT)\n" " \"blocktime\" : ttt (numeric) The block time in seconds since epoch (Jan 1 1970 GMT)\n" + " \"time\" : ttt, (numeric) Same as \"blocktime\"\n" "}\n" "\nExamples:\n" @@ -207,19 +206,21 @@ static UniValue gettxoutproof(const JSONRPCRequest& request) { if (request.fHelp || (request.params.size() != 1 && request.params.size() != 2)) throw std::runtime_error( - "gettxoutproof [\"txid\",...] ( blockhash )\n" - "\nReturns a hex-encoded proof that \"txid\" was included in a block.\n" - "\nNOTE: By default this function only works sometimes. This is when there is an\n" - "unspent output in the utxo for this transaction. To make it always work,\n" - "you need to maintain a transaction index, using the -txindex command line option or\n" - "specify the block in which the transaction is included manually (by blockhash).\n" - "\nArguments:\n" - "1. \"txids\" (string) A json array of txids to filter\n" - " [\n" - " \"txid\" (string) A transaction hash\n" - " ,...\n" - " ]\n" - "2. \"blockhash\" (string, optional) If specified, looks for txid in the block with this hash\n" + RPCHelpMan{"gettxoutproof", + "\nReturns a hex-encoded proof that \"txid\" was included in a block.\n" + "\nNOTE: By default this function only works sometimes. This is when there is an\n" + "unspent output in the utxo for this transaction. To make it always work,\n" + "you need to maintain a transaction index, using the -txindex command line option or\n" + "specify the block in which the transaction is included manually (by blockhash).\n", + { + {"txids", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "A json array of txids to filter", + { + {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "A transaction hash"}, + }, + }, + {"blockhash", RPCArg::Type::STR_HEX, /* opt */ true, /* default_val */ "null", "If specified, looks for txid in the block with this hash"}, + }} + .ToString() + "\nResult:\n" "\"data\" (string) A string that is a serialized, hex-encoded data for the proof.\n" ); @@ -229,9 +230,7 @@ static UniValue gettxoutproof(const JSONRPCRequest& request) UniValue txids = request.params[0].get_array(); for (unsigned int idx = 0; idx < txids.size(); idx++) { const UniValue& txid = txids[idx]; - if (txid.get_str().length() != 64 || !IsHex(txid.get_str())) - throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid txid ")+txid.get_str()); - uint256 hash(uint256S(txid.get_str())); + uint256 hash(ParseHashV(txid, "txid")); if (setTxids.count(hash)) throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated txid: ")+txid.get_str()); setTxids.insert(hash); @@ -242,7 +241,7 @@ static UniValue gettxoutproof(const JSONRPCRequest& request) uint256 hashBlock; if (!request.params[1].isNull()) { LOCK(cs_main); - hashBlock = uint256S(request.params[1].get_str()); + hashBlock = ParseHashV(request.params[1], "blockhash"); pblockindex = LookupBlockIndex(hashBlock); if (!pblockindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); @@ -301,11 +300,13 @@ static UniValue verifytxoutproof(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 1) throw std::runtime_error( - "verifytxoutproof \"proof\"\n" - "\nVerifies that a proof points to a transaction in a block, returning the transaction it commits to\n" - "and throwing an RPC error if the block is not in our best chain\n" - "\nArguments:\n" - "1. \"proof\" (string, required) The hex-encoded proof generated by gettxoutproof\n" + RPCHelpMan{"verifytxoutproof", + "\nVerifies that a proof points to a transaction in a block, returning the transaction it commits to\n" + "and throwing an RPC error if the block is not in our best chain\n", + { + {"proof", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The hex-encoded proof generated by gettxoutproof"}, + }} + .ToString() + "\nResult:\n" "[\"txid\"] (array, strings) The txid(s) which the proof commits to, or empty array if the proof can not be validated.\n" ); @@ -351,7 +352,7 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal if (!locktime.isNull()) { int64_t nLockTime = locktime.get_int64(); - if (nLockTime < 0 || nLockTime > std::numeric_limits<uint32_t>::max()) + if (nLockTime < 0 || nLockTime > LOCKTIME_MAX) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, locktime out of range"); rawTx.nLockTime = nLockTime; } @@ -373,18 +374,18 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal uint32_t nSequence; if (rbfOptIn) { - nSequence = MAX_BIP125_RBF_SEQUENCE; + nSequence = MAX_BIP125_RBF_SEQUENCE; /* CTxIn::SEQUENCE_FINAL - 2 */ } else if (rawTx.nLockTime) { - nSequence = std::numeric_limits<uint32_t>::max() - 1; + nSequence = CTxIn::SEQUENCE_FINAL - 1; } else { - nSequence = std::numeric_limits<uint32_t>::max(); + nSequence = CTxIn::SEQUENCE_FINAL; } // set the sequence number if passed in the parameters object const UniValue& sequenceObj = find_value(o, "sequence"); if (sequenceObj.isNum()) { int64_t seqNr64 = sequenceObj.get_int64(); - if (seqNr64 < 0 || seqNr64 > std::numeric_limits<uint32_t>::max()) { + if (seqNr64 < 0 || seqNr64 > CTxIn::SEQUENCE_FINAL) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, sequence number is out of range"); } else { nSequence = (uint32_t)seqNr64; @@ -396,7 +397,6 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal rawTx.vin.push_back(in); } - std::set<CTxDestination> destinations; if (!outputs_is_obj) { // Translate array of key-value pairs into dict UniValue outputs_dict = UniValue(UniValue::VOBJ); @@ -412,8 +412,17 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal } outputs = std::move(outputs_dict); } + + // Duplicate checking + std::set<CTxDestination> destinations; + bool has_data{false}; + for (const std::string& name_ : outputs.getKeys()) { if (name_ == "data") { + if (has_data) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, duplicate key: data"); + } + has_data = true; std::vector<unsigned char> data = ParseHexV(outputs[name_].getValStr(), "Data"); CTxOut out(0, CScript() << OP_RETURN << data); @@ -447,38 +456,46 @@ static UniValue createrawtransaction(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 2 || request.params.size() > 4) { throw std::runtime_error( - // clang-format off - "createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] [{\"address\":amount},{\"data\":\"hex\"},...] ( locktime ) ( replaceable )\n" - "\nCreate a transaction spending the given inputs and creating new outputs.\n" - "Outputs can be addresses or data.\n" - "Returns hex-encoded raw transaction.\n" - "Note that the transaction's inputs are not signed, and\n" - "it is not stored in the wallet or transmitted to the network.\n" - - "\nArguments:\n" - "1. \"inputs\" (array, required) A json array of json objects\n" - " [\n" - " {\n" - " \"txid\":\"id\", (string, required) The transaction id\n" - " \"vout\":n, (numeric, required) The output number\n" - " \"sequence\":n (numeric, optional) The sequence number\n" - " } \n" - " ,...\n" - " ]\n" - "2. \"outputs\" (array, required) a json array with outputs (key-value pairs)\n" - " [\n" - " {\n" - " \"address\": x.xxx, (obj, optional) A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT + "\n" - " },\n" - " {\n" - " \"data\": \"hex\" (obj, optional) A key-value pair. The key must be \"data\", the value is hex encoded data\n" - " }\n" - " ,... More key-value pairs of the above form. For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n" - " accepted as second parameter.\n" - " ]\n" - "3. locktime (numeric, optional, default=0) Raw locktime. Non-0 value also locktime-activates inputs\n" - "4. replaceable (boolean, optional, default=false) Marks this transaction as BIP125 replaceable.\n" - " Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible.\n" + RPCHelpMan{"createrawtransaction", + "\nCreate a transaction spending the given inputs and creating new outputs.\n" + "Outputs can be addresses or data.\n" + "Returns hex-encoded raw transaction.\n" + "Note that the transaction's inputs are not signed, and\n" + "it is not stored in the wallet or transmitted to the network.\n", + { + {"inputs", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "A json array of json objects", + { + {"", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "", "", + { + {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The transaction id"}, + {"vout", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "The output number"}, + {"sequence", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "depends on the value of the 'replaceable' and 'locktime' arguments", "The sequence number"}, + }, + }, + }, + }, + {"outputs", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "a json array with outputs (key-value pairs), where none of the keys are duplicated.\n" + "That is, each address can only appear once and there can only be one 'data' object.\n" + "For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n" + " accepted as second parameter.", + { + {"", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "", "", + { + {"address", RPCArg::Type::AMOUNT, /* opt */ false, /* default_val */ "", "A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT}, + }, + }, + {"", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "", "", + { + {"data", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "A key-value pair. The key must be \"data\", the value is hex-encoded data"}, + }, + }, + }, + }, + {"locktime", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "0", "Raw locktime. Non-0 value also locktime-activates inputs"}, + {"replaceable", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Marks this transaction as BIP125-replaceable.\n" + " Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible."}, + }} + .ToString() + "\nResult:\n" "\"transaction\" (string) hex string of the transaction\n" @@ -487,7 +504,6 @@ static UniValue createrawtransaction(const JSONRPCRequest& request) + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"data\\\":\\\"00010203\\\"}]\"") + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"[{\\\"address\\\":0.01}]\"") + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"[{\\\"data\\\":\\\"00010203\\\"}]\"") - // clang-format on ); } @@ -508,14 +524,14 @@ static UniValue decoderawtransaction(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) throw std::runtime_error( - "decoderawtransaction \"hexstring\" ( iswitness )\n" - "\nReturn a JSON object representing the serialized, hex-encoded transaction.\n" - - "\nArguments:\n" - "1. \"hexstring\" (string, required) The transaction hex string\n" - "2. iswitness (boolean, optional) Whether the transaction hex is a serialized witness transaction\n" - " If iswitness is not present, heuristic tests will be used in decoding\n" - + RPCHelpMan{"decoderawtransaction", + "\nReturn a JSON object representing the serialized, hex-encoded transaction.\n", + { + {"hexstring", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The transaction hex string"}, + {"iswitness", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "depends on heuristic tests", "Whether the transaction hex is a serialized witness transaction\n" + " If iswitness is not present, heuristic tests will be used in decoding"}, + }} + .ToString() + "\nResult:\n" "{\n" " \"txid\" : \"id\", (string) The transaction id\n" @@ -583,14 +599,16 @@ static UniValue decodescript(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 1) throw std::runtime_error( - "decodescript \"hexstring\"\n" - "\nDecode a hex-encoded script.\n" - "\nArguments:\n" - "1. \"hexstring\" (string) the hex encoded script\n" + RPCHelpMan{"decodescript", + "\nDecode a hex-encoded script.\n", + { + {"hexstring", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "the hex-encoded script"}, + }} + .ToString() + "\nResult:\n" "{\n" " \"asm\":\"asm\", (string) Script public key\n" - " \"hex\":\"hex\", (string) hex encoded public key\n" + " \"hex\":\"hex\", (string) hex-encoded public key\n" " \"type\":\"type\", (string) The output type\n" " \"reqSigs\": n, (numeric) The required signatures\n" " \"addresses\": [ (json array of string)\n" @@ -626,9 +644,8 @@ static UniValue decodescript(const JSONRPCRequest& request) // P2SH and witness programs cannot be wrapped in P2WSH, if this script // is a witness program, don't return addresses for a segwit programs. if (type.get_str() == "pubkey" || type.get_str() == "pubkeyhash" || type.get_str() == "multisig" || type.get_str() == "nonstandard") { - txnouttype which_type; std::vector<std::vector<unsigned char>> solutions_data; - Solver(script, which_type, solutions_data); + txnouttype which_type = Solver(script, solutions_data); // Uncompressed pubkeys cannot be used with segwit checksigs. // If the script contains an uncompressed pubkey, skip encoding of a segwit program. if ((which_type == TX_PUBKEY) || (which_type == TX_MULTISIG)) { @@ -677,21 +694,20 @@ static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std:: static UniValue combinerawtransaction(const JSONRPCRequest& request) { - if (request.fHelp || request.params.size() != 1) throw std::runtime_error( - "combinerawtransaction [\"hexstring\",...]\n" - "\nCombine multiple partially signed transactions into one transaction.\n" - "The combined transaction may be another partially signed transaction or a \n" - "fully signed transaction." - - "\nArguments:\n" - "1. \"txs\" (string) A json array of hex strings of partially signed transactions\n" - " [\n" - " \"hexstring\" (string) A transaction hash\n" - " ,...\n" - " ]\n" - + RPCHelpMan{"combinerawtransaction", + "\nCombine multiple partially signed transactions into one transaction.\n" + "The combined transaction may be another partially signed transaction or a \n" + "fully signed transaction.", + { + {"txs", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "A json array of hex strings of partially signed transactions", + { + {"hexstring", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "A transaction hash"}, + }, + }, + }} + .ToString() + "\nResult:\n" "\"hex\" (string) The hex-encoded raw transaction with signature(s)\n" @@ -760,7 +776,7 @@ static UniValue combinerawtransaction(const JSONRPCRequest& request) return EncodeHexTx(mergedTx); } -UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival, CBasicKeyStore *keystore, bool is_temp_keystore, const UniValue& hashType) +UniValue SignTransaction(interfaces::Chain& chain, CMutableTransaction& mtx, const UniValue& prevTxsUnival, CBasicKeyStore *keystore, bool is_temp_keystore, const UniValue& hashType) { // Fetch previous transactions (inputs): CCoinsView viewDummy; @@ -825,8 +841,7 @@ UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival view.AddCoin(out, std::move(newcoin), true); } - // if redeemScript given and not using the local wallet (private keys - // given), add redeemScript to the keystore so it can be signed: + // if redeemScript and private keys were given, add redeemScript to the keystore so it can be signed if (is_temp_keystore && (scriptPubKey.IsPayToScriptHash() || scriptPubKey.IsPayToWitnessScriptHash())) { RPCTypeCheckObj(prevOut, { @@ -904,39 +919,42 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 2 || request.params.size() > 4) throw std::runtime_error( - "signrawtransactionwithkey \"hexstring\" [\"privatekey1\",...] ( [{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\",\"redeemScript\":\"hex\"},...] sighashtype )\n" - "\nSign inputs for raw transaction (serialized, hex-encoded).\n" - "The second argument is an array of base58-encoded private\n" - "keys that will be the only keys used to sign the transaction.\n" - "The third optional argument (may be null) is an array of previous transaction outputs that\n" - "this transaction depends on but may not yet be in the block chain.\n" - - "\nArguments:\n" - "1. \"hexstring\" (string, required) The transaction hex string\n" - "2. \"privkeys\" (string, required) A json array of base58-encoded private keys for signing\n" - " [ (json array of strings)\n" - " \"privatekey\" (string) private key in base58-encoding\n" - " ,...\n" - " ]\n" - "3. \"prevtxs\" (string, optional) An json array of previous dependent transaction outputs\n" - " [ (json array of json objects, or 'null' if none provided)\n" - " {\n" - " \"txid\":\"id\", (string, required) The transaction id\n" - " \"vout\":n, (numeric, required) The output number\n" - " \"scriptPubKey\": \"hex\", (string, required) script key\n" - " \"redeemScript\": \"hex\", (string, required for P2SH or P2WSH) redeem script\n" - " \"amount\": value (numeric, required) The amount spent\n" - " }\n" - " ,...\n" - " ]\n" - "4. \"sighashtype\" (string, optional, default=ALL) The signature hash type. Must be one of\n" + RPCHelpMan{"signrawtransactionwithkey", + "\nSign inputs for raw transaction (serialized, hex-encoded).\n" + "The second argument is an array of base58-encoded private\n" + "keys that will be the only keys used to sign the transaction.\n" + "The third optional argument (may be null) is an array of previous transaction outputs that\n" + "this transaction depends on but may not yet be in the block chain.\n", + { + {"hexstring", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The transaction hex string"}, + {"privkeys", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "A json array of base58-encoded private keys for signing", + { + {"privatekey", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "private key in base58-encoding"}, + }, + }, + {"prevtxs", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "null", "A json array of previous dependent transaction outputs", + { + {"", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "", "", + { + {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The transaction id"}, + {"vout", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "The output number"}, + {"scriptPubKey", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "script key"}, + {"redeemScript", RPCArg::Type::STR_HEX, /* opt */ true, /* default_val */ "omitted", "(required for P2SH or P2WSH) redeem script"}, + {"amount", RPCArg::Type::AMOUNT, /* opt */ false, /* default_val */ "", "The amount spent"}, + }, + }, + }, + }, + {"sighashtype", RPCArg::Type::STR, /* opt */ true, /* default_val */ "ALL", "The signature hash type. Must be one of:\n" " \"ALL\"\n" " \"NONE\"\n" " \"SINGLE\"\n" " \"ALL|ANYONECANPAY\"\n" " \"NONE|ANYONECANPAY\"\n" " \"SINGLE|ANYONECANPAY\"\n" - + }, + }} + .ToString() + "\nResult:\n" "{\n" " \"hex\" : \"value\", (string) The hex-encoded raw transaction with signature(s)\n" @@ -976,129 +994,39 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request) keystore.AddKey(key); } - return SignTransaction(mtx, request.params[2], &keystore, true, request.params[3]); + return SignTransaction(*g_rpc_interfaces->chain, mtx, request.params[2], &keystore, true, request.params[3]); } UniValue signrawtransaction(const JSONRPCRequest& request) { -#ifdef ENABLE_WALLET - std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); - CWallet* const pwallet = wallet.get(); -#endif - - if (request.fHelp || request.params.size() < 1 || request.params.size() > 4) - throw std::runtime_error( - "signrawtransaction \"hexstring\" ( [{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\",\"redeemScript\":\"hex\"},...] [\"privatekey1\",...] sighashtype )\n" - "\nDEPRECATED. Sign inputs for raw transaction (serialized, hex-encoded).\n" - "The second optional argument (may be null) is an array of previous transaction outputs that\n" - "this transaction depends on but may not yet be in the block chain.\n" - "The third optional argument (may be null) is an array of base58-encoded private\n" - "keys that, if given, will be the only keys used to sign the transaction.\n" -#ifdef ENABLE_WALLET - + HelpRequiringPassphrase(pwallet) + "\n" -#endif - "\nArguments:\n" - "1. \"hexstring\" (string, required) The transaction hex string\n" - "2. \"prevtxs\" (string, optional) An json array of previous dependent transaction outputs\n" - " [ (json array of json objects, or 'null' if none provided)\n" - " {\n" - " \"txid\":\"id\", (string, required) The transaction id\n" - " \"vout\":n, (numeric, required) The output number\n" - " \"scriptPubKey\": \"hex\", (string, required) script key\n" - " \"redeemScript\": \"hex\", (string, required for P2SH or P2WSH) redeem script\n" - " \"amount\": value (numeric, required) The amount spent\n" - " }\n" - " ,...\n" - " ]\n" - "3. \"privkeys\" (string, optional) A json array of base58-encoded private keys for signing\n" - " [ (json array of strings, or 'null' if none provided)\n" - " \"privatekey\" (string) private key in base58-encoding\n" - " ,...\n" - " ]\n" - "4. \"sighashtype\" (string, optional, default=ALL) The signature hash type. Must be one of\n" - " \"ALL\"\n" - " \"NONE\"\n" - " \"SINGLE\"\n" - " \"ALL|ANYONECANPAY\"\n" - " \"NONE|ANYONECANPAY\"\n" - " \"SINGLE|ANYONECANPAY\"\n" - - "\nResult:\n" - "{\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" - - "\nExamples:\n" - + HelpExampleCli("signrawtransaction", "\"myhex\"") - + HelpExampleRpc("signrawtransaction", "\"myhex\"") - ); - - if (!IsDeprecatedRPCEnabled("signrawtransaction")) { - throw JSONRPCError(RPC_METHOD_DEPRECATED, "signrawtransaction is deprecated and will be fully removed in v0.18. " - "To use signrawtransaction in v0.17, restart bitcoind with -deprecatedrpc=signrawtransaction.\n" - "Projects should transition to using signrawtransactionwithkey and signrawtransactionwithwallet before upgrading to v0.18"); - } - - RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR, UniValue::VARR, UniValue::VSTR}, true); - - // Make a JSONRPCRequest to pass on to the right signrawtransaction* command - JSONRPCRequest new_request; - new_request.id = request.id; - new_request.params.setArray(); - - // For signing with private keys - if (!request.params[2].isNull()) { - new_request.params.push_back(request.params[0]); - // Note: the prevtxs and privkeys are reversed for signrawtransactionwithkey - new_request.params.push_back(request.params[2]); - new_request.params.push_back(request.params[1]); - new_request.params.push_back(request.params[3]); - return signrawtransactionwithkey(new_request); - } else { -#ifdef ENABLE_WALLET - // Otherwise sign with the wallet which does not take a privkeys parameter - new_request.params.push_back(request.params[0]); - new_request.params.push_back(request.params[1]); - new_request.params.push_back(request.params[3]); - return signrawtransactionwithwallet(new_request); -#else - // If we have made it this far, then wallet is disabled and no private keys were given, so fail here. - throw JSONRPCError(RPC_INVALID_PARAMETER, "No private keys available."); -#endif - } + // This method should be removed entirely in V0.19, along with the entries in the + // CRPCCommand table and rpc/client.cpp. + throw JSONRPCError(RPC_METHOD_DEPRECATED, "signrawtransaction was removed in v0.18.\n" + "Clients should transition to using signrawtransactionwithkey and signrawtransactionwithwallet"); } static UniValue sendrawtransaction(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) throw std::runtime_error( - "sendrawtransaction \"hexstring\" ( allowhighfees )\n" - "\nSubmits raw transaction (serialized, hex-encoded) to local node and network.\n" - "\nAlso see createrawtransaction and signrawtransaction calls.\n" - "\nArguments:\n" - "1. \"hexstring\" (string, required) The hex string of the raw transaction)\n" - "2. allowhighfees (boolean, optional, default=false) Allow high fees\n" + RPCHelpMan{"sendrawtransaction", + "\nSubmits raw transaction (serialized, hex-encoded) to local node and network.\n" + "\nAlso see createrawtransaction and signrawtransactionwithkey calls.\n", + { + {"hexstring", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The hex string of the raw transaction"}, + {"allowhighfees", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Allow high fees"}, + }} + .ToString() + "\nResult:\n" "\"hex\" (string) The transaction hash in hex\n" "\nExamples:\n" "\nCreate a transaction\n" + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\" : \\\"mytxid\\\",\\\"vout\\\":0}]\" \"{\\\"myaddress\\\":0.01}\"") + "Sign the transaction, and get back the hex\n" - + HelpExampleCli("signrawtransaction", "\"myhex\"") + + + HelpExampleCli("signrawtransactionwithwallet", "\"myhex\"") + "\nSend the transaction (signed hex)\n" + HelpExampleCli("sendrawtransaction", "\"signedhex\"") + - "\nAs a json rpc call\n" + "\nAs a JSON-RPC call\n" + HelpExampleRpc("sendrawtransaction", "\"signedhex\"") ); @@ -1178,15 +1106,20 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { throw std::runtime_error( - // clang-format off - "testmempoolaccept [\"rawtxs\"] ( allowhighfees )\n" - "\nReturns if raw transaction (serialized, hex-encoded) would be accepted by mempool.\n" - "\nThis checks if the transaction violates the consensus or policy rules.\n" - "\nSee sendrawtransaction call.\n" - "\nArguments:\n" - "1. [\"rawtxs\"] (array, required) An array of hex strings of raw transactions.\n" - " Length must be one for now.\n" - "2. allowhighfees (boolean, optional, default=false) Allow high fees\n" + RPCHelpMan{"testmempoolaccept", + "\nReturns result of mempool acceptance tests indicating if raw transaction (serialized, hex-encoded) would be accepted by mempool.\n" + "\nThis checks if the transaction violates the consensus or policy rules.\n" + "\nSee sendrawtransaction call.\n", + { + {"rawtxs", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "An array of hex strings of raw transactions.\n" + " Length must be one for now.", + { + {"rawtx", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", ""}, + }, + }, + {"allowhighfees", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Allow high fees"}, + }} + .ToString() + "\nResult:\n" "[ (array) The result of the mempool acceptance test for each raw transaction in the input array.\n" " Length is exactly one for now.\n" @@ -1200,12 +1133,11 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request) "\nCreate a transaction\n" + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\" : \\\"mytxid\\\",\\\"vout\\\":0}]\" \"{\\\"myaddress\\\":0.01}\"") + "Sign the transaction, and get back the hex\n" - + HelpExampleCli("signrawtransaction", "\"myhex\"") + + + HelpExampleCli("signrawtransactionwithwallet", "\"myhex\"") + "\nTest acceptance of the transaction (signed hex)\n" - + HelpExampleCli("testmempoolaccept", "\"signedhex\"") + - "\nAs a json rpc call\n" + + HelpExampleCli("testmempoolaccept", "[\"signedhex\"]") + + "\nAs a JSON-RPC call\n" + HelpExampleRpc("testmempoolaccept", "[\"signedhex\"]") - // clang-format on ); } @@ -1276,12 +1208,12 @@ UniValue decodepsbt(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 1) throw std::runtime_error( - "decodepsbt \"psbt\"\n" - "\nReturn a JSON object representing the serialized, base64-encoded partially signed Bitcoin transaction.\n" - - "\nArguments:\n" - "1. \"psbt\" (string, required) The PSBT base64 string\n" - + RPCHelpMan{"decodepsbt", + "\nReturn a JSON object representing the serialized, base64-encoded partially signed Bitcoin transaction.\n", + { + {"psbt", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The PSBT base64 string"}, + }} + .ToString() + "\nResult:\n" "{\n" " \"tx\" : { (json object) The decoded network-serialized unsigned transaction.\n" @@ -1459,11 +1391,8 @@ UniValue decodepsbt(const JSONRPCRequest& request) UniValue keypath(UniValue::VOBJ); keypath.pushKV("pubkey", HexStr(entry.first)); - uint32_t fingerprint = entry.second.at(0); - keypath.pushKV("master_fingerprint", strprintf("%08x", bswap_32(fingerprint))); - - entry.second.erase(entry.second.begin()); - keypath.pushKV("path", WriteHDKeypath(entry.second)); + keypath.pushKV("master_fingerprint", strprintf("%08x", ReadBE32(entry.second.fingerprint))); + keypath.pushKV("path", WriteHDKeypath(entry.second.path)); keypaths.push_back(keypath); } in.pushKV("bip32_derivs", keypaths); @@ -1521,12 +1450,8 @@ UniValue decodepsbt(const JSONRPCRequest& request) for (auto entry : output.hd_keypaths) { UniValue keypath(UniValue::VOBJ); keypath.pushKV("pubkey", HexStr(entry.first)); - - uint32_t fingerprint = entry.second.at(0); - keypath.pushKV("master_fingerprint", strprintf("%08x", bswap_32(fingerprint))); - - entry.second.erase(entry.second.begin()); - keypath.pushKV("path", WriteHDKeypath(entry.second)); + keypath.pushKV("master_fingerprint", strprintf("%08x", ReadBE32(entry.second.fingerprint))); + keypath.pushKV("path", WriteHDKeypath(entry.second.path)); keypaths.push_back(keypath); } out.pushKV("bip32_derivs", keypaths); @@ -1558,16 +1483,17 @@ UniValue combinepsbt(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 1) throw std::runtime_error( - "combinepsbt [\"psbt\",...]\n" - "\nCombine multiple partially signed Bitcoin transactions into one transaction.\n" - "Implements the Combiner role.\n" - "\nArguments:\n" - "1. \"txs\" (string) A json array of base64 strings of partially signed transactions\n" - " [\n" - " \"psbt\" (string) A base64 string of a PSBT\n" - " ,...\n" - " ]\n" - + RPCHelpMan{"combinepsbt", + "\nCombine multiple partially signed Bitcoin transactions into one transaction.\n" + "Implements the Combiner role.\n", + { + {"txs", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "A json array of base64 strings of partially signed transactions", + { + {"psbt", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "A base64 string of a PSBT"}, + }, + }, + }} + .ToString() + "\nResult:\n" " \"psbt\" (string) The base64-encoded partially signed transaction\n" "\nExamples:\n" @@ -1611,16 +1537,17 @@ UniValue finalizepsbt(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) throw std::runtime_error( - "finalizepsbt \"psbt\" ( extract )\n" - "Finalize the inputs of a PSBT. If the transaction is fully signed, it will produce a\n" - "network serialized transaction which can be broadcast with sendrawtransaction. Otherwise a PSBT will be\n" - "created which has the final_scriptSig and final_scriptWitness fields filled for inputs that are complete.\n" - "Implements the Finalizer and Extractor roles.\n" - "\nArguments:\n" - "1. \"psbt\" (string) A base64 string of a PSBT\n" - "2. \"extract\" (boolean, optional, default=true) If true and the transaction is complete, \n" - " extract and return the complete transaction in normal network serialization instead of the PSBT.\n" - + RPCHelpMan{"finalizepsbt", + "Finalize the inputs of a PSBT. If the transaction is fully signed, it will produce a\n" + "network serialized transaction which can be broadcast with sendrawtransaction. Otherwise a PSBT will be\n" + "created which has the final_scriptSig and final_scriptWitness fields filled for inputs that are complete.\n" + "Implements the Finalizer and Extractor roles.\n", + { + {"psbt", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "A base64 string of a PSBT"}, + {"extract", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "true", "If true and the transaction is complete,\n" + " extract and return the complete transaction in normal network serialization instead of the PSBT."}, + }} + .ToString() + "\nResult:\n" "{\n" " \"psbt\" : \"value\", (string) The base64-encoded partially signed transaction if not extracted\n" @@ -1642,13 +1569,13 @@ UniValue finalizepsbt(const JSONRPCRequest& request) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error)); } - // Get all of the previous transactions + // Finalize input signatures -- in case we have partial signatures that add up to a complete + // signature, but have not combined them yet (e.g. because the combiner that created this + // PartiallySignedTransaction did not understand them), this will combine them into a final + // script. bool complete = true; for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) { - PSBTInput& input = psbtx.inputs.at(i); - - SignatureData sigdata; - complete &= SignPSBTInput(DUMMY_SIGNING_PROVIDER, *psbtx.tx, input, sigdata, i, 1); + complete &= SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, SIGHASH_ALL); } UniValue result(UniValue::VOBJ); @@ -1661,10 +1588,10 @@ UniValue finalizepsbt(const JSONRPCRequest& request) mtx.vin[i].scriptWitness = psbtx.inputs[i].final_script_witness; } ssTx << mtx; - result.pushKV("hex", HexStr(ssTx.begin(), ssTx.end())); + result.pushKV("hex", HexStr(ssTx.str())); } else { ssTx << psbtx; - result.pushKV("psbt", EncodeBase64((unsigned char*)ssTx.data(), ssTx.size())); + result.pushKV("psbt", EncodeBase64(ssTx.str())); } result.pushKV("complete", complete); @@ -1675,33 +1602,43 @@ UniValue createpsbt(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 2 || request.params.size() > 4) throw std::runtime_error( - "createpsbt [{\"txid\":\"id\",\"vout\":n},...] [{\"address\":amount},{\"data\":\"hex\"},...] ( locktime ) ( replaceable )\n" - "\nCreates a transaction in the Partially Signed Transaction format.\n" - "Implements the Creator role.\n" - "\nArguments:\n" - "1. \"inputs\" (array, required) A json array of json objects\n" - " [\n" - " {\n" - " \"txid\":\"id\", (string, required) The transaction id\n" - " \"vout\":n, (numeric, required) The output number\n" - " \"sequence\":n (numeric, optional) The sequence number\n" - " } \n" - " ,...\n" - " ]\n" - "2. \"outputs\" (array, required) a json array with outputs (key-value pairs)\n" - " [\n" - " {\n" - " \"address\": x.xxx, (obj, optional) A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT + "\n" - " },\n" - " {\n" - " \"data\": \"hex\" (obj, optional) A key-value pair. The key must be \"data\", the value is hex encoded data\n" - " }\n" - " ,... More key-value pairs of the above form. For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n" - " accepted as second parameter.\n" - " ]\n" - "3. locktime (numeric, optional, default=0) Raw locktime. Non-0 value also locktime-activates inputs\n" - "4. replaceable (boolean, optional, default=false) Marks this transaction as BIP125 replaceable.\n" - " Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible.\n" + RPCHelpMan{"createpsbt", + "\nCreates a transaction in the Partially Signed Transaction format.\n" + "Implements the Creator role.\n", + { + {"inputs", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "A json array of json objects", + { + {"", RPCArg::Type::OBJ, /* opt */ false, /* default_val */ "", "", + { + {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The transaction id"}, + {"vout", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "The output number"}, + {"sequence", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "depends on the value of the 'replaceable' and 'locktime' arguments", "The sequence number"}, + }, + }, + }, + }, + {"outputs", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "a json array with outputs (key-value pairs), where none of the keys are duplicated.\n" + "That is, each address can only appear once and there can only be one 'data' object.\n" + "For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n" + " accepted as second parameter.", + { + {"", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "", "", + { + {"address", RPCArg::Type::AMOUNT, /* opt */ false, /* default_val */ "", "A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT}, + }, + }, + {"", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "", "", + { + {"data", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "A key-value pair. The key must be \"data\", the value is hex-encoded data"}, + }, + }, + }, + }, + {"locktime", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "0", "Raw locktime. Non-0 value also locktime-activates inputs"}, + {"replaceable", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Marks this transaction as BIP125 replaceable.\n" + " Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible."}, + }} + .ToString() + "\nResult:\n" " \"psbt\" (string) The resulting raw transaction (base64-encoded string)\n" "\nExamples:\n" @@ -1740,17 +1677,19 @@ UniValue converttopsbt(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 3) throw std::runtime_error( - "converttopsbt \"hexstring\" ( permitsigdata iswitness )\n" - "\nConverts a network serialized transaction to a PSBT. This should be used only with createrawtransaction and fundrawtransaction\n" - "createpsbt and walletcreatefundedpsbt should be used for new applications.\n" - "\nArguments:\n" - "1. \"hexstring\" (string, required) The hex string of a raw transaction\n" - "2. permitsigdata (boolean, optional, default=false) If true, any signatures in the input will be discarded and conversion.\n" - " will continue. If false, RPC will fail if any signatures are present.\n" - "3. iswitness (boolean, optional) Whether the transaction hex is a serialized witness transaction.\n" + RPCHelpMan{"converttopsbt", + "\nConverts a network serialized transaction to a PSBT. This should be used only with createrawtransaction and fundrawtransaction\n" + "createpsbt and walletcreatefundedpsbt should be used for new applications.\n", + { + {"hexstring", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The hex string of a raw transaction"}, + {"permitsigdata", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "If true, any signatures in the input will be discarded and conversion.\n" + " will continue. If false, RPC will fail if any signatures are present."}, + {"iswitness", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "depends on heuristic tests", "Whether the transaction hex is a serialized witness transaction.\n" " If iswitness is not present, heuristic tests will be used in decoding. If true, only witness deserializaion\n" - " will be tried. If false, only non-witness deserialization wil be tried. Only has an effect if\n" - " permitsigdata is true.\n" + " will be tried. If false, only non-witness deserialization will be tried. Only has an effect if\n" + " permitsigdata is true."}, + }} + .ToString() + "\nResult:\n" " \"psbt\" (string) The resulting raw transaction (base64-encoded string)\n" "\nExamples:\n" @@ -1776,7 +1715,7 @@ UniValue converttopsbt(const JSONRPCRequest& request) // Remove all scriptSigs and scriptWitnesses from inputs for (CTxIn& input : tx.vin) { - if ((!input.scriptSig.empty() || !input.scriptWitness.IsNull()) && (request.params[1].isNull() || (!request.params[1].isNull() && request.params[1].get_bool()))) { + if ((!input.scriptSig.empty() || !input.scriptWitness.IsNull()) && !permitsigdata) { throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Inputs must not have scriptSigs and scriptWitnesses"); } input.scriptSig.clear(); @@ -1800,6 +1739,7 @@ UniValue converttopsbt(const JSONRPCRequest& request) return EncodeBase64((unsigned char*)ssTx.data(), ssTx.size()); } +// clang-format off static const CRPCCommand commands[] = { // category name actor (function) argNames // --------------------- ------------------------ ----------------------- ---------- @@ -1809,7 +1749,7 @@ static const CRPCCommand commands[] = { "rawtransactions", "decodescript", &decodescript, {"hexstring"} }, { "rawtransactions", "sendrawtransaction", &sendrawtransaction, {"hexstring","allowhighfees"} }, { "rawtransactions", "combinerawtransaction", &combinerawtransaction, {"txs"} }, - { "rawtransactions", "signrawtransaction", &signrawtransaction, {"hexstring","prevtxs","privkeys","sighashtype"} }, /* uses wallet if enabled */ + { "hidden", "signrawtransaction", &signrawtransaction, {"hexstring","prevtxs","privkeys","sighashtype"} }, { "rawtransactions", "signrawtransactionwithkey", &signrawtransactionwithkey, {"hexstring","privkeys","prevtxs","sighashtype"} }, { "rawtransactions", "testmempoolaccept", &testmempoolaccept, {"rawtxs","allowhighfees"} }, { "rawtransactions", "decodepsbt", &decodepsbt, {"psbt"} }, @@ -1821,6 +1761,7 @@ static const CRPCCommand commands[] = { "blockchain", "gettxoutproof", &gettxoutproof, {"txids", "blockhash"} }, { "blockchain", "verifytxoutproof", &verifytxoutproof, {"proof"} }, }; +// clang-format on void RegisterRawTransactionRPCCommands(CRPCTable &t) { diff --git a/src/rpc/rawtransaction.h b/src/rpc/rawtransaction.h index 924611ed5a..52d701d1c3 100644 --- a/src/rpc/rawtransaction.h +++ b/src/rpc/rawtransaction.h @@ -9,8 +9,12 @@ class CBasicKeyStore; struct CMutableTransaction; class UniValue; +namespace interfaces { +class Chain; +} // namespace interfaces + /** Sign a transaction with the given keystore and previous transactions */ -UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxs, CBasicKeyStore *keystore, bool tempKeystore, const UniValue& hashType); +UniValue SignTransaction(interfaces::Chain& chain, CMutableTransaction& mtx, const UniValue& prevTxs, CBasicKeyStore *keystore, bool tempKeystore, const UniValue& hashType); /** Create a transaction from univalue parameters */ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& rbf); diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index e46bf2f765..e7e047334e 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -8,15 +8,14 @@ #include <fs.h> #include <key_io.h> #include <random.h> +#include <rpc/util.h> #include <shutdown.h> #include <sync.h> #include <ui_interface.h> -#include <util.h> -#include <utilstrencodings.h> +#include <util/strencodings.h> +#include <util/system.h> -#include <boost/bind.hpp> #include <boost/signals2/signal.hpp> -#include <boost/algorithm/string/case_conv.hpp> // for to_upper() #include <boost/algorithm/string/classification.hpp> #include <boost/algorithm/string/split.hpp> @@ -24,7 +23,7 @@ #include <unordered_map> static CCriticalSection cs_rpcWarmup; -static bool fRPCRunning = false; +static std::atomic<bool> g_rpc_running{false}; static bool fRPCInWarmup GUARDED_BY(cs_rpcWarmup) = true; static std::string rpcWarmupStatus GUARDED_BY(cs_rpcWarmup) = "RPC server started"; /* Timer-creating functions */ @@ -117,16 +116,12 @@ CAmount AmountFromValue(const UniValue& value) uint256 ParseHashV(const UniValue& v, std::string strName) { - std::string strHex; - if (v.isStr()) - strHex = v.get_str(); + std::string strHex(v.get_str()); + if (64 != strHex.length()) + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s must be of length %d (not %d, for '%s')", strName, 64, strHex.length(), strHex)); if (!IsHex(strHex)) // Note: IsHex("") is false throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')"); - if (64 != strHex.length()) - throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s must be of length %d (not %d)", strName, 64, strHex.length())); - uint256 result; - result.SetHex(strHex); - return result; + return uint256S(strHex); } uint256 ParseHashO(const UniValue& o, std::string strKey) { @@ -146,10 +141,6 @@ std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey) return ParseHexV(find_value(o, strKey), strKey); } -/** - * Note: This interface may still be subject to change. - */ - std::string CRPCTable::help(const std::string& strCommand, const JSONRPCRequest& helpreq) const { std::string strRet; @@ -192,9 +183,7 @@ std::string CRPCTable::help(const std::string& strCommand, const JSONRPCRequest& if (!category.empty()) strRet += "\n"; category = pcmd->category; - std::string firstLetter = category.substr(0,1); - boost::to_upper(firstLetter); - strRet += "== " + firstLetter + category.substr(1) + " ==\n"; + strRet += "== " + Capitalize(category) + " ==\n"; } } strRet += strHelp + "\n"; @@ -210,10 +199,12 @@ UniValue help(const JSONRPCRequest& jsonRequest) { if (jsonRequest.fHelp || jsonRequest.params.size() > 1) throw std::runtime_error( - "help ( \"command\" )\n" - "\nList all commands, or get help for a specified command.\n" - "\nArguments:\n" - "1. \"command\" (string, optional) The command to get help on\n" + RPCHelpMan{"help", + "\nList all commands, or get help for a specified command.\n", + { + {"command", RPCArg::Type::STR, /* opt */ true, /* default_val */ "all commands", "The command to get help on"}, + }} + .ToString() + "\nResult:\n" "\"text\" (string) The help text\n" ); @@ -229,22 +220,30 @@ UniValue help(const JSONRPCRequest& jsonRequest) UniValue stop(const JSONRPCRequest& jsonRequest) { // Accept the deprecated and ignored 'detach' boolean argument + // Also accept the hidden 'wait' integer argument (milliseconds) + // For instance, 'stop 1000' makes the call wait 1 second before returning + // to the client (intended for testing) if (jsonRequest.fHelp || jsonRequest.params.size() > 1) throw std::runtime_error( - "stop\n" - "\nStop Bitcoin server."); + RPCHelpMan{"stop", + "\nStop Bitcoin server.", {}} + .ToString()); // Event loop will exit after current HTTP requests have been handled, so // this reply will get back to the client. StartShutdown(); + if (jsonRequest.params[0].isNum()) { + MilliSleep(jsonRequest.params[0].get_int()); + } return "Bitcoin server stopping"; } static UniValue uptime(const JSONRPCRequest& jsonRequest) { - if (jsonRequest.fHelp || jsonRequest.params.size() > 1) + if (jsonRequest.fHelp || jsonRequest.params.size() > 0) throw std::runtime_error( - "uptime\n" - "\nReturns the total uptime of the server.\n" + RPCHelpMan{"uptime", + "\nReturns the total uptime of the server.\n", {}} + .ToString() + "\nResult:\n" "ttt (numeric) The number of seconds that the server has been running\n" "\nExamples:\n" @@ -255,17 +254,16 @@ static UniValue uptime(const JSONRPCRequest& jsonRequest) return GetTime() - GetStartupTime(); } -/** - * Call Table - */ +// clang-format off static const CRPCCommand vRPCCommands[] = { // category name actor (function) argNames // --------------------- ------------------------ ----------------------- ---------- /* Overall control/query calls */ { "control", "help", &help, {"command"} }, - { "control", "stop", &stop, {} }, + { "control", "stop", &stop, {"wait"} }, { "control", "uptime", &uptime, {} }, }; +// clang-format on CRPCTable::CRPCTable() { @@ -304,7 +302,7 @@ bool CRPCTable::appendCommand(const std::string& name, const CRPCCommand* pcmd) void StartRPC() { LogPrint(BCLog::RPC, "Starting RPC\n"); - fRPCRunning = true; + g_rpc_running = true; g_rpcSignals.Started(); } @@ -312,7 +310,7 @@ void InterruptRPC() { LogPrint(BCLog::RPC, "Interrupting RPC\n"); // Interrupt e.g. running longpolls - fRPCRunning = false; + g_rpc_running = false; } void StopRPC() @@ -325,7 +323,7 @@ void StopRPC() bool IsRPCRunning() { - return fRPCRunning; + return g_rpc_running; } void SetRPCWarmupStatus(const std::string& newStatus) @@ -505,11 +503,7 @@ UniValue CRPCTable::execute(const JSONRPCRequest &request) const std::vector<std::string> CRPCTable::listCommands() const { std::vector<std::string> commandList; - typedef std::map<std::string, const CRPCCommand*> commandMap; - - std::transform( mapCommands.begin(), mapCommands.end(), - std::back_inserter(commandList), - boost::bind(&commandMap::value_type::first,_1) ); + for (const auto& i : mapCommands) commandList.emplace_back(i.first); return commandList; } @@ -541,7 +535,7 @@ void RPCUnsetTimerInterface(RPCTimerInterface *iface) timerInterface = nullptr; } -void RPCRunLater(const std::string& name, std::function<void(void)> func, int64_t nSeconds) +void RPCRunLater(const std::string& name, std::function<void()> func, int64_t nSeconds) { if (!timerInterface) throw JSONRPCError(RPC_INTERNAL_ERROR, "No timer handler registered for RPC"); diff --git a/src/rpc/server.h b/src/rpc/server.h index 15d0ec80f5..2d62a76f3c 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -110,7 +110,7 @@ public: * This is needed to cope with the case in which there is no HTTP server, but * only GUI RPC console, and to break the dependency of pcserver on httprpc. */ - virtual RPCTimerBase* NewTimer(std::function<void(void)>& func, int64_t millis) = 0; + virtual RPCTimerBase* NewTimer(std::function<void()>& func, int64_t millis) = 0; }; /** Set the factory function for timers */ @@ -124,7 +124,7 @@ void RPCUnsetTimerInterface(RPCTimerInterface *iface); * Run func nSeconds from now. * Overrides previous timer <name> (if any). */ -void RPCRunLater(const std::string& name, std::function<void(void)> func, int64_t nSeconds); +void RPCRunLater(const std::string& name, std::function<void()> func, int64_t nSeconds); typedef UniValue(*rpcfn_type)(const JSONRPCRequest& jsonRequest); diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp index ba414bf3f5..b91baee4ac 100644 --- a/src/rpc/util.cpp +++ b/src/rpc/util.cpp @@ -7,7 +7,9 @@ #include <rpc/protocol.h> #include <rpc/util.h> #include <tinyformat.h> -#include <utilstrencodings.h> +#include <util/strencodings.h> + +InitInterfaces* g_rpc_interfaces = nullptr; // Converts a hex string to a public key if possible CPubKey HexToPubKey(const std::string& hex_in) @@ -126,3 +128,304 @@ UniValue DescribeAddress(const CTxDestination& dest) { return boost::apply_visitor(DescribeAddressVisitor(), dest); } + +struct Section { + Section(const std::string& left, const std::string& right) + : m_left{left}, m_right{right} {} + const std::string m_left; + const std::string m_right; +}; + +struct Sections { + std::vector<Section> m_sections; + size_t m_max_pad{0}; + + void PushSection(const Section& s) + { + m_max_pad = std::max(m_max_pad, s.m_left.size()); + 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) + { + const auto indent = std::string(current_indent, ' '); + const auto indent_next = std::string(current_indent + 2, ' '); + switch (arg.m_type) { + case RPCArg::Type::STR_HEX: + case RPCArg::Type::STR: + case RPCArg::Type::NUM: + case RPCArg::Type::AMOUNT: + case RPCArg::Type::BOOL: { + if (outer_type == OuterType::NAMED_ARG) 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); + } else { + left += outer_type == OuterType::OBJ ? arg.ToStringObj(/* oneline */ false) : arg.ToString(/* oneline */ false); + } + left += ","; + PushSection({left, arg.ToDescriptionString(/* implicitly_required */ outer_type == OuterType::ARR)}); + break; + } + case RPCArg::Type::OBJ: + case RPCArg::Type::OBJ_USER_KEYS: { + const auto right = outer_type == OuterType::NAMED_ARG ? "" : arg.ToDescriptionString(/* implicitly_required */ outer_type == OuterType::ARR); + PushSection({indent + "{", 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 ? "," : ""), ""}); + break; + } + case RPCArg::Type::ARR: { + auto left = indent; + left += outer_type == OuterType::OBJ ? "\"" + arg.m_name + "\": " : ""; + left += "["; + const auto right = outer_type == OuterType::NAMED_ARG ? "" : arg.ToDescriptionString(/* implicitly_required */ outer_type == OuterType::ARR); + 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 ? "," : ""), ""}); + break; + } + + // no default case, so the compiler can warn about missing cases + } + } + + std::string ToString() const + { + std::string ret; + const size_t pad = m_max_pad + 4; + for (const auto& s : m_sections) { + if (s.m_right.empty()) { + ret += s.m_left; + ret += "\n"; + continue; + } + + std::string left = s.m_left; + left.resize(pad, ' '); + ret += left; + + // Properly pad after newlines + std::string right; + size_t begin = 0; + size_t new_line_pos = s.m_right.find_first_of('\n'); + while (true) { + right += s.m_right.substr(begin, new_line_pos - begin); + if (new_line_pos == std::string::npos) { + break; //No new line + } + right += "\n" + std::string(pad, ' '); + begin = s.m_right.find_first_not_of(' ', new_line_pos + 1); + if (begin == std::string::npos) { + break; // Empty line + } + new_line_pos = s.m_right.find_first_of('\n', begin + 1); + } + ret += right; + ret += "\n"; + } + return ret; + } +}; + +RPCHelpMan::RPCHelpMan(const std::string& name, const std::string& description, const std::vector<RPCArg>& args) + : m_name{name}, m_description{description}, m_args{args} +{ + std::set<std::string> named_args; + for (const auto& arg : m_args) { + // Should have unique named arguments + assert(named_args.insert(arg.m_name).second); + } +} + +std::string RPCHelpMan::ToString() const +{ + std::string ret; + + // Oneline summary + ret += m_name; + bool was_optional{false}; + for (const auto& arg : m_args) { + ret += " "; + if (arg.m_optional) { + if (!was_optional) ret += "( "; + was_optional = true; + } else { + if (was_optional) ret += ") "; + was_optional = false; + } + ret += arg.ToString(/* oneline */ true); + } + if (was_optional) ret += " )"; + ret += "\n"; + + // Description + ret += m_description; + + // Arguments + Sections sections; + for (size_t i{0}; i < m_args.size(); ++i) { + const auto& arg = m_args.at(i); + + 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_max_pad = std::max(sections.m_max_pad, sections.m_sections.back().m_left.size()); + + // Recursively push nested args + sections.Push(arg); + } + ret += sections.ToString(); + + return ret; +} + +std::string RPCArg::ToDescriptionString(const bool implicitly_required) const +{ + std::string ret; + ret += "("; + if (m_type_str.size() != 0) { + ret += m_type_str.at(1); + } else { + switch (m_type) { + case Type::STR_HEX: + case Type::STR: { + ret += "string"; + break; + } + case Type::NUM: { + ret += "numeric"; + break; + } + case Type::AMOUNT: { + ret += "numeric or string"; + break; + } + case Type::BOOL: { + ret += "boolean"; + break; + } + case Type::OBJ: + case Type::OBJ_USER_KEYS: { + ret += "json object"; + break; + } + case Type::ARR: { + ret += "json array"; + break; + } + + // no default case, so the compiler can warn about missing cases + } + } + if (!implicitly_required) { + ret += ", "; + if (m_optional) { + ret += "optional"; + if (!m_default_value.empty()) { + ret += ", default=" + m_default_value; + } else { + // TODO enable this assert, when all optional parameters have their default value documented + //assert(false); + } + } else { + ret += "required"; + assert(m_default_value.empty()); // Default value is ignored, and must not be present + } + } + ret += ")"; + ret += m_description.empty() ? "" : " " + m_description; + return ret; +} + +std::string RPCArg::ToStringObj(const bool oneline) const +{ + std::string res; + res += "\""; + res += m_name; + if (oneline) { + res += "\":"; + } else { + res += "\": "; + } + switch (m_type) { + case Type::STR: + return res + "\"str\""; + case Type::STR_HEX: + return res + "\"hex\""; + case Type::NUM: + return res + "n"; + case Type::AMOUNT: + return res + "amount"; + case Type::BOOL: + return res + "bool"; + case Type::ARR: + res += "["; + for (const auto& i : m_inner) { + res += i.ToString(oneline) + ","; + } + return res + "...]"; + 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); +} + +std::string RPCArg::ToString(const bool oneline) const +{ + if (oneline && !m_oneline_description.empty()) return m_oneline_description; + + switch (m_type) { + case Type::STR_HEX: + case Type::STR: { + return "\"" + m_name + "\""; + } + case Type::NUM: + case Type::AMOUNT: + case Type::BOOL: { + return m_name; + } + case Type::OBJ: + case Type::OBJ_USER_KEYS: { + std::string res; + for (size_t i = 0; i < m_inner.size();) { + res += m_inner[i].ToStringObj(oneline); + if (++i < m_inner.size()) res += ","; + } + if (m_type == Type::OBJ) { + return "{" + res + "}"; + } else { + return "{" + res + ",...}"; + } + } + case Type::ARR: { + std::string res; + for (const auto& i : m_inner) { + res += i.ToString(oneline) + ","; + } + return "[" + res + "...]"; + } + + // no default case, so the compiler can warn about missing cases + } + assert(false); +} diff --git a/src/rpc/util.h b/src/rpc/util.h index 0a3a156e45..9e97b3ae0b 100644 --- a/src/rpc/util.h +++ b/src/rpc/util.h @@ -9,14 +9,18 @@ #include <script/standard.h> #include <univalue.h> -#include <boost/variant/static_visitor.hpp> - #include <string> #include <vector> class CKeyStore; class CPubKey; class CScript; +struct InitInterfaces; + +//! Pointers to interfaces that need to be accessible from RPC methods. Due to +//! limitations of the RPC framework, there's currently no direct way to pass in +//! state to RPC method implementations. +extern InitInterfaces* g_rpc_interfaces; CPubKey HexToPubKey(const std::string& hex_in); CPubKey AddrToPubKey(CKeyStore* const keystore, const std::string& addr_in); @@ -24,4 +28,95 @@ CScript CreateMultisigRedeemscript(const int required, const std::vector<CPubKey UniValue DescribeAddress(const CTxDestination& dest); +struct RPCArg { + enum class Type { + OBJ, + ARR, + STR, + NUM, + BOOL, + OBJ_USER_KEYS, //!< Special type where the user must set the keys e.g. to define multiple addresses; as opposed to e.g. an options object where the keys are predefined + AMOUNT, //!< Special type representing a floating point amount (can be either NUM or STR) + STR_HEX, //!< Special type that is a STR with only hex chars + }; + const std::string m_name; //!< The name of the arg (can be empty for inner args) + const Type m_type; + const std::vector<RPCArg> m_inner; //!< Only used for arrays or dicts + const bool m_optional; + const std::string m_default_value; //!< Only used for optional args + const std::string m_description; + const std::string m_oneline_description; //!< Should be empty unless it is supposed to override the auto-generated summary line + 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 bool opt, + const std::string& default_val, + const std::string& description, + const std::string& oneline_description = "", + const std::vector<std::string>& type_str = {}) + : m_name{name}, + m_type{type}, + m_optional{opt}, + m_default_value{default_val}, + m_description{description}, + m_oneline_description{oneline_description}, + m_type_str{type_str} + { + assert(type != Type::ARR && type != Type::OBJ); + } + + RPCArg( + const std::string& name, + const Type& type, + const bool opt, + const std::string& default_val, + 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_optional{opt}, + m_default_value{default_val}, + m_description{description}, + m_oneline_description{oneline_description}, + m_type_str{type_str} + { + assert(type == Type::ARR || type == Type::OBJ); + } + + /** + * Return the type string of the argument. + * Set oneline to allow it to be overridden by a custom oneline type string (m_oneline_description). + */ + std::string ToString(bool oneline) const; + /** + * Return the type string of the argument when it is in an object (dict). + * Set oneline to get the oneline representation (less whitespace) + */ + std::string ToStringObj(bool oneline) const; + /** + * Return the description string, including the argument type and whether + * the argument is required. + * implicitly_required is set for arguments in an array, which are neither optional nor required. + */ + std::string ToDescriptionString(bool implicitly_required = false) const; +}; + +class RPCHelpMan +{ +public: + RPCHelpMan(const std::string& name, const std::string& description, const std::vector<RPCArg>& args); + + std::string ToString() const; + +private: + const std::string m_name; + const std::string m_description; + const std::vector<RPCArg> m_args; +}; + #endif // BITCOIN_RPC_UTIL_H |