diff options
Diffstat (limited to 'src/rpc')
-rw-r--r-- | src/rpc/blockchain.cpp | 627 | ||||
-rw-r--r-- | src/rpc/client.cpp | 1 | ||||
-rw-r--r-- | src/rpc/mining.cpp | 216 | ||||
-rw-r--r-- | src/rpc/misc.cpp | 173 | ||||
-rw-r--r-- | src/rpc/net.cpp | 278 | ||||
-rw-r--r-- | src/rpc/rawtransaction.cpp | 577 | ||||
-rw-r--r-- | src/rpc/rawtransaction_util.cpp | 56 | ||||
-rw-r--r-- | src/rpc/rawtransaction_util.h | 4 | ||||
-rw-r--r-- | src/rpc/server.cpp | 28 | ||||
-rw-r--r-- | src/rpc/util.cpp | 115 | ||||
-rw-r--r-- | src/rpc/util.h | 149 |
11 files changed, 1239 insertions, 985 deletions
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index eb5148eebd..133d1b809f 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -31,7 +31,6 @@ #include <undo.h> #include <util/strencodings.h> #include <util/system.h> -#include <util/validation.h> #include <validation.h> #include <validationinterface.h> #include <warnings.h> @@ -178,8 +177,7 @@ static UniValue getblockcount(const JSONRPCRequest& request) "The genesis block has height 0.\n", {}, RPCResult{ - "n (numeric) The current block count\n" - }, + RPCResult::Type::NUM, "", "The current block count"}, RPCExamples{ HelpExampleCli("getblockcount", "") + HelpExampleRpc("getblockcount", "") @@ -196,8 +194,7 @@ static UniValue getbestblockhash(const JSONRPCRequest& request) "\nReturns the hash of the best (tip) block in the most-work fully-validated chain.\n", {}, RPCResult{ - "\"hex\" (string) the block hash, hex-encoded\n" - }, + RPCResult::Type::STR_HEX, "", "the block hash, hex-encoded"}, RPCExamples{ HelpExampleCli("getbestblockhash", "") + HelpExampleRpc("getbestblockhash", "") @@ -227,11 +224,11 @@ static UniValue waitfornewblock(const JSONRPCRequest& request) {"timeout", RPCArg::Type::NUM, /* default */ "0", "Time in milliseconds to wait for a response. 0 indicates no timeout."}, }, RPCResult{ - "{ (json object)\n" - " \"hash\" : { (string) The blockhash\n" - " \"height\" : { (int) Block height\n" - "}\n" - }, + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "hash", "The blockhash"}, + {RPCResult::Type::NUM, "height", "Block height"}, + }}, RPCExamples{ HelpExampleCli("waitfornewblock", "1000") + HelpExampleRpc("waitfornewblock", "1000") @@ -267,11 +264,11 @@ static UniValue waitforblock(const JSONRPCRequest& request) {"timeout", RPCArg::Type::NUM, /* default */ "0", "Time in milliseconds to wait for a response. 0 indicates no timeout."}, }, RPCResult{ - "{ (json object)\n" - " \"hash\" : { (string) The blockhash\n" - " \"height\" : { (int) Block height\n" - "}\n" - }, + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "hash", "The blockhash"}, + {RPCResult::Type::NUM, "height", "Block height"}, + }}, RPCExamples{ HelpExampleCli("waitforblock", "\"0000000000079f8ef3d2c688c244eb7a4570b24c9ed7b4a8c619eb02596f8862\", 1000") + HelpExampleRpc("waitforblock", "\"0000000000079f8ef3d2c688c244eb7a4570b24c9ed7b4a8c619eb02596f8862\", 1000") @@ -311,11 +308,11 @@ static UniValue waitforblockheight(const JSONRPCRequest& request) {"timeout", RPCArg::Type::NUM, /* default */ "0", "Time in milliseconds to wait for a response. 0 indicates no timeout."}, }, RPCResult{ - "{ (json object)\n" - " \"hash\" : { (string) The blockhash\n" - " \"height\" : { (int) Block height\n" - "}\n" - }, + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "hash", "The blockhash"}, + {RPCResult::Type::NUM, "height", "Block height"}, + }}, RPCExamples{ HelpExampleCli("waitforblockheight", "\"100\", 1000") + HelpExampleRpc("waitforblockheight", "\"100\", 1000") @@ -365,8 +362,7 @@ static UniValue getdifficulty(const JSONRPCRequest& request) "\nReturns the proof-of-work difficulty as a multiple of the minimum difficulty.\n", {}, RPCResult{ - "n.nnn (numeric) the proof-of-work difficulty as a multiple of the minimum difficulty.\n" - }, + RPCResult::Type::NUM, "", "the proof-of-work difficulty as a multiple of the minimum difficulty."}, RPCExamples{ HelpExampleCli("getdifficulty", "") + HelpExampleRpc("getdifficulty", "") @@ -377,37 +373,35 @@ static UniValue getdifficulty(const JSONRPCRequest& request) return GetDifficulty(::ChainActive().Tip()); } -static std::string EntryDescriptionString() -{ - return " \"vsize\" : n, (numeric) virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted.\n" - " \"size\" : n, (numeric) (DEPRECATED) same as vsize. Only returned if bitcoind is started with -deprecatedrpc=size\n" - " size will be completely removed in v0.20.\n" - " \"weight\" : n, (numeric) transaction weight as defined in BIP 141.\n" - " \"fee\" : n, (numeric) transaction fee in " + CURRENCY_UNIT + " (DEPRECATED)\n" - " \"modifiedfee\" : n, (numeric) transaction fee with fee deltas used for mining priority (DEPRECATED)\n" - " \"time\" : n, (numeric) local time transaction entered pool in seconds since 1 Jan 1970 GMT\n" - " \"height\" : n, (numeric) block height when transaction entered pool\n" - " \"descendantcount\" : n, (numeric) number of in-mempool descendant transactions (including this one)\n" - " \"descendantsize\" : n, (numeric) virtual transaction size of in-mempool descendants (including this one)\n" - " \"descendantfees\" : n, (numeric) modified fees (see above) of in-mempool descendants (including this one) (DEPRECATED)\n" - " \"ancestorcount\" : n, (numeric) number of in-mempool ancestor transactions (including this one)\n" - " \"ancestorsize\" : n, (numeric) virtual transaction size of in-mempool ancestors (including this one)\n" - " \"ancestorfees\" : n, (numeric) modified fees (see above) of in-mempool ancestors (including this one) (DEPRECATED)\n" - " \"wtxid\" : hash, (string) hash of serialized transaction, including witness data\n" - " \"fees\" : {\n" - " \"base\" : n, (numeric) transaction fee in " + CURRENCY_UNIT + "\n" - " \"modified\" : n, (numeric) transaction fee with fee deltas used for mining priority in " + CURRENCY_UNIT + "\n" - " \"ancestor\" : n, (numeric) modified fees (see above) of in-mempool ancestors (including this one) in " + CURRENCY_UNIT + "\n" - " \"descendant\" : n, (numeric) modified fees (see above) of in-mempool descendants (including this one) in " + CURRENCY_UNIT + "\n" - " }\n" - " \"depends\" : [ (array) unconfirmed transactions used as inputs for this transaction\n" - " \"transactionid\", (string) parent transaction id\n" - " ... ]\n" - " \"spentby\" : [ (array) unconfirmed transactions spending outputs from this transaction\n" - " \"transactionid\", (string) child transaction id\n" - " ... ]\n" - " \"bip125-replaceable\" : true|false, (boolean) Whether this transaction could be replaced due to BIP125 (replace-by-fee)\n"; -} +static std::vector<RPCResult> MempoolEntryDescription() { return { + RPCResult{RPCResult::Type::NUM, "vsize", "virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted."}, + RPCResult{RPCResult::Type::NUM, "size", "(DEPRECATED) same as vsize. Only returned if bitcoind is started with -deprecatedrpc=size\n" + "size will be completely removed in v0.20."}, + RPCResult{RPCResult::Type::NUM, "weight", "transaction weight as defined in BIP 141."}, + RPCResult{RPCResult::Type::STR_AMOUNT, "fee", "transaction fee in " + CURRENCY_UNIT + " (DEPRECATED)"}, + RPCResult{RPCResult::Type::STR_AMOUNT, "modifiedfee", "transaction fee with fee deltas used for mining priority (DEPRECATED)"}, + RPCResult{RPCResult::Type::NUM_TIME, "time", "local time transaction entered pool in seconds since 1 Jan 1970 GMT"}, + RPCResult{RPCResult::Type::NUM, "height", "block height when transaction entered pool"}, + RPCResult{RPCResult::Type::NUM, "descendantcount", "number of in-mempool descendant transactions (including this one)"}, + RPCResult{RPCResult::Type::NUM, "descendantsize", "virtual transaction size of in-mempool descendants (including this one)"}, + RPCResult{RPCResult::Type::STR_AMOUNT, "descendantfees", "modified fees (see above) of in-mempool descendants (including this one) (DEPRECATED)"}, + RPCResult{RPCResult::Type::NUM, "ancestorcount", "number of in-mempool ancestor transactions (including this one)"}, + RPCResult{RPCResult::Type::NUM, "ancestorsize", "virtual transaction size of in-mempool ancestors (including this one)"}, + RPCResult{RPCResult::Type::STR_AMOUNT, "ancestorfees", "modified fees (see above) of in-mempool ancestors (including this one) (DEPRECATED)"}, + RPCResult{RPCResult::Type::STR_HEX, "wtxid", "hash of serialized transaction, including witness data"}, + RPCResult{RPCResult::Type::OBJ, "fees", "", + { + RPCResult{RPCResult::Type::STR_AMOUNT, "base", "transaction fee in " + CURRENCY_UNIT}, + RPCResult{RPCResult::Type::STR_AMOUNT, "modified", "transaction fee with fee deltas used for mining priority in " + CURRENCY_UNIT}, + RPCResult{RPCResult::Type::STR_AMOUNT, "ancestor", "modified fees (see above) of in-mempool ancestors (including this one) in " + CURRENCY_UNIT}, + RPCResult{RPCResult::Type::STR_AMOUNT, "descendant", "modified fees (see above) of in-mempool descendants (including this one) in " + CURRENCY_UNIT}, + }}, + RPCResult{RPCResult::Type::ARR, "depends", "unconfirmed transactions used as inputs for this transaction", + {RPCResult{RPCResult::Type::STR_HEX, "transactionid", "parent transaction id"}}}, + RPCResult{RPCResult::Type::ARR, "spentby", "unconfirmed transactions spending outputs from this transaction", + {RPCResult{RPCResult::Type::STR_HEX, "transactionid", "child transaction id"}}}, + RPCResult{RPCResult::Type::BOOL, "bip125-replaceable", "Whether this transaction could be replaced due to BIP125 (replace-by-fee)"}, +};} static void entryToJSON(const CTxMemPool& pool, UniValue& info, const CTxMemPoolEntry& e) EXCLUSIVE_LOCKS_REQUIRED(pool.cs) { @@ -506,17 +500,17 @@ static UniValue getrawmempool(const JSONRPCRequest& request) { {"verbose", RPCArg::Type::BOOL, /* default */ "false", "True for a json object, false for array of transaction ids"}, }, - RPCResult{"for verbose = false", - "[ (json array of string)\n" - " \"transactionid\" (string) The transaction id\n" - " ,...\n" - "]\n" - "\nResult: (for verbose = true):\n" - "{ (json object)\n" - " \"transactionid\" : { (json object)\n" - + EntryDescriptionString() - + " }, ...\n" - "}\n" + { + RPCResult{"for verbose = false", + RPCResult::Type::ARR, "", "", + { + {RPCResult::Type::STR_HEX, "", "The transaction id"}, + }}, + RPCResult{"for verbose = true", + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::OBJ_DYN, "transactionid", "", MempoolEntryDescription()}, + }}, }, RPCExamples{ HelpExampleCli("getrawmempool", "true") @@ -541,18 +535,10 @@ static UniValue getmempoolancestors(const JSONRPCRequest& request) }, { RPCResult{"for verbose = false", - "[ (json array of strings)\n" - " \"transactionid\" (string) The transaction id of an in-mempool ancestor transaction\n" - " ,...\n" - "]\n" - }, + RPCResult::Type::ARR, "", "", + {{RPCResult::Type::STR_HEX, "", "The transaction id of an in-mempool ancestor transaction"}}}, RPCResult{"for verbose = true", - "{ (json object)\n" - " \"transactionid\" : { (json object)\n" - + EntryDescriptionString() - + " }, ...\n" - "}\n" - }, + RPCResult::Type::OBJ_DYN, "transactionid", "", MempoolEntryDescription()}, }, RPCExamples{ HelpExampleCli("getmempoolancestors", "\"mytxid\"") @@ -609,18 +595,13 @@ static UniValue getmempooldescendants(const JSONRPCRequest& request) }, { RPCResult{"for verbose = false", - "[ (json array of strings)\n" - " \"transactionid\" (string) The transaction id of an in-mempool descendant transaction\n" - " ,...\n" - "]\n" - }, + RPCResult::Type::ARR, "", "", + {{RPCResult::Type::STR_HEX, "", "The transaction id of an in-mempool descendant transaction"}}}, RPCResult{"for verbose = true", - "{ (json object)\n" - " \"transactionid\" : { (json object)\n" - + EntryDescriptionString() - + " }, ...\n" - "}\n" - }, + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::OBJ_DYN, "transactionid", "", MempoolEntryDescription()}, + }}, }, RPCExamples{ HelpExampleCli("getmempooldescendants", "\"mytxid\"") @@ -675,10 +656,7 @@ static UniValue getmempoolentry(const JSONRPCRequest& request) {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id (must be in mempool)"}, }, RPCResult{ - "{ (json object)\n" - + EntryDescriptionString() - + "}\n" - }, + RPCResult::Type::OBJ_DYN, "", "", MempoolEntryDescription()}, RPCExamples{ HelpExampleCli("getmempoolentry", "\"mytxid\"") + HelpExampleRpc("getmempoolentry", "\"mytxid\"") @@ -709,8 +687,7 @@ static UniValue getblockhash(const JSONRPCRequest& request) {"height", RPCArg::Type::NUM, RPCArg::Optional::NO, "The height index"}, }, RPCResult{ - "\"hash\" (string) The block hash\n" - }, + RPCResult::Type::STR_HEX, "", "The block hash"}, RPCExamples{ HelpExampleCli("getblockhash", "1000") + HelpExampleRpc("getblockhash", "1000") @@ -738,27 +715,26 @@ static UniValue getblockheader(const JSONRPCRequest& request) }, { RPCResult{"for verbose = true", - "{\n" - " \"hash\" : \"hash\", (string) the block hash (same as provided)\n" - " \"confirmations\" : n, (numeric) The number of confirmations, or -1 if the block is not on the main chain\n" - " \"height\" : n, (numeric) The block height or index\n" - " \"version\" : n, (numeric) The block version\n" - " \"versionHex\" : \"00000000\", (string) The block version formatted in hexadecimal\n" - " \"merkleroot\" : \"xxxx\", (string) The merkle root\n" - " \"time\" : ttt, (numeric) The block time expressed in " + UNIX_EPOCH_TIME + "\n" - " \"mediantime\" : ttt, (numeric) The median block time expressed in " + UNIX_EPOCH_TIME + "\n" - " \"nonce\" : n, (numeric) The nonce\n" - " \"bits\" : \"1d00ffff\", (string) The bits\n" - " \"difficulty\" : x.xxx, (numeric) The difficulty\n" - " \"chainwork\" : \"0000...1f3\" (string) Expected number of hashes required to produce the current chain (in hex)\n" - " \"nTx\" : n, (numeric) The number of transactions in the block.\n" - " \"previousblockhash\" : \"hash\", (string) The hash of the previous block\n" - " \"nextblockhash\" : \"hash\", (string) The hash of the next block\n" - "}\n" - }, + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "hash", "the block hash (same as provided)"}, + {RPCResult::Type::NUM, "confirmations", "The number of confirmations, or -1 if the block is not on the main chain"}, + {RPCResult::Type::NUM, "height", "The block height or index"}, + {RPCResult::Type::NUM, "version", "The block version"}, + {RPCResult::Type::STR_HEX, "versionHex", "The block version formatted in hexadecimal"}, + {RPCResult::Type::STR_HEX, "merkleroot", "The merkle root"}, + {RPCResult::Type::NUM_TIME, "time", "The block time expressed in " + UNIX_EPOCH_TIME}, + {RPCResult::Type::NUM_TIME, "mediantime", "The median block time expressed in " + UNIX_EPOCH_TIME}, + {RPCResult::Type::NUM, "nonce", "The nonce"}, + {RPCResult::Type::STR_HEX, "bits", "The bits"}, + {RPCResult::Type::NUM, "difficulty", "The difficulty"}, + {RPCResult::Type::STR_HEX, "chainwork", "Expected number of hashes required to produce the current chain"}, + {RPCResult::Type::NUM, "nTx", "The number of transactions in the block"}, + {RPCResult::Type::STR_HEX, "previousblockhash", "The hash of the previous block"}, + {RPCResult::Type::STR_HEX, "nextblockhash", "The hash of the next block"}, + }}, RPCResult{"for verbose=false", - "\"data\" (string) A string that is serialized, hex-encoded data for block 'hash'.\n" - }, + RPCResult::Type::STR_HEX, "", "A string that is serialized, hex-encoded data for block 'hash'"}, }, RPCExamples{ HelpExampleCli("getblockheader", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"") @@ -840,44 +816,45 @@ static UniValue getblock(const JSONRPCRequest& request) }, { RPCResult{"for verbosity = 0", - "\"data\" (string) A string that is serialized, hex-encoded data for block 'hash'.\n" - }, + RPCResult::Type::STR_HEX, "", "A string that is serialized, hex-encoded data for block 'hash'"}, RPCResult{"for verbosity = 1", - "{\n" - " \"hash\" : \"hash\", (string) the block hash (same as provided)\n" - " \"confirmations\" : n, (numeric) The number of confirmations, or -1 if the block is not on the main chain\n" - " \"size\" : n, (numeric) The block size\n" - " \"strippedsize\" : n, (numeric) The block size excluding witness data\n" - " \"weight\" : n (numeric) The block weight as defined in BIP 141\n" - " \"height\" : n, (numeric) The block height or index\n" - " \"version\" : n, (numeric) The block version\n" - " \"versionHex\" : \"00000000\", (string) The block version formatted in hexadecimal\n" - " \"merkleroot\" : \"xxxx\", (string) The merkle root\n" - " \"tx\" : [ (array of string) The transaction ids\n" - " \"transactionid\" (string) The transaction id\n" - " ,...\n" - " ],\n" - " \"time\" : ttt, (numeric) The block time expressed in " + UNIX_EPOCH_TIME + "\n" - " \"mediantime\" : ttt, (numeric) The median block time expressed in " + UNIX_EPOCH_TIME + "\n" - " \"nonce\" : n, (numeric) The nonce\n" - " \"bits\" : \"1d00ffff\", (string) The bits\n" - " \"difficulty\" : x.xxx, (numeric) The difficulty\n" - " \"chainwork\" : \"xxxx\", (string) Expected number of hashes required to produce the chain up to this block (in hex)\n" - " \"nTx\" : n, (numeric) The number of transactions in the block.\n" - " \"previousblockhash\" : \"hash\", (string) The hash of the previous block\n" - " \"nextblockhash\" : \"hash\" (string) The hash of the next block\n" - "}\n" - }, + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "hash", "the block hash (same as provided)"}, + {RPCResult::Type::NUM, "confirmations", "The number of confirmations, or -1 if the block is not on the main chain"}, + {RPCResult::Type::NUM, "size", "The block size"}, + {RPCResult::Type::NUM, "strippedsize", "The block size excluding witness data"}, + {RPCResult::Type::NUM, "weight", "The block weight as defined in BIP 141"}, + {RPCResult::Type::NUM, "height", "The block height or index"}, + {RPCResult::Type::NUM, "version", "The block version"}, + {RPCResult::Type::STR_HEX, "versionHex", "The block version formatted in hexadecimal"}, + {RPCResult::Type::STR_HEX, "merkleroot", "The merkle root"}, + {RPCResult::Type::ARR, "tx", "The transaction ids", + {{RPCResult::Type::STR_HEX, "", "The transaction id"}}}, + {RPCResult::Type::NUM_TIME, "time", "The block time expressed in " + UNIX_EPOCH_TIME}, + {RPCResult::Type::NUM_TIME, "mediantime", "The median block time expressed in " + UNIX_EPOCH_TIME}, + {RPCResult::Type::NUM, "nonce", "The nonce"}, + {RPCResult::Type::STR_HEX, "bits", "The bits"}, + {RPCResult::Type::NUM, "difficulty", "The difficulty"}, + {RPCResult::Type::STR_HEX, "chainwork", "Expected number of hashes required to produce the chain up to this block (in hex)"}, + {RPCResult::Type::NUM, "nTx", "The number of transactions in the block"}, + {RPCResult::Type::STR_HEX, "previousblockhash", "The hash of the previous block"}, + {RPCResult::Type::STR_HEX, "nextblockhash", "The hash of the next block"}, + }}, RPCResult{"for verbosity = 2", - "{\n" - " ..., Same output as verbosity = 1.\n" - " \"tx\" : [ (array of Objects) The transactions in the format of the getrawtransaction RPC. Different from verbosity = 1 \"tx\" result.\n" - " ,...\n" - " ],\n" - " ,... Same output as verbosity = 1.\n" - "}\n" - }, - }, + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::ELISION, "", "Same output as verbosity = 1"}, + {RPCResult::Type::ARR, "tx", "", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::ELISION, "", "The transactions in the format of the getrawtransaction RPC. Different from verbosity = 1 \"tx\" result"}, + }}, + }}, + {RPCResult::Type::ELISION, "", "Same output as verbosity = 1"}, + }}, + }, RPCExamples{ HelpExampleCli("getblock", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"") + HelpExampleRpc("getblock", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"") @@ -928,8 +905,7 @@ static UniValue pruneblockchain(const JSONRPCRequest& request) " to prune blocks whose block time is at least 2 hours older than the provided timestamp."}, }, RPCResult{ - "n (numeric) Height of the last block pruned.\n" - }, + RPCResult::Type::NUM, "", "Height of the last block pruned"}, RPCExamples{ HelpExampleCli("pruneblockchain", "1000") + HelpExampleRpc("pruneblockchain", "1000") @@ -983,17 +959,17 @@ static UniValue gettxoutsetinfo(const JSONRPCRequest& request) "Note this call may take some time.\n", {}, RPCResult{ - "{\n" - " \"height\":n, (numeric) The current block height (index)\n" - " \"bestblock\": \"hex\", (string) The hash of the block at the tip of the chain\n" - " \"transactions\": n, (numeric) The number of transactions with unspent outputs\n" - " \"txouts\": n, (numeric) The number of unspent transaction outputs\n" - " \"bogosize\": n, (numeric) A meaningless metric for UTXO set size\n" - " \"hash_serialized_2\": \"hash\", (string) The serialized hash\n" - " \"disk_size\": n, (numeric) The estimated size of the chainstate on disk\n" - " \"total_amount\": x.xxx (numeric) The total amount\n" - "}\n" - }, + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::NUM, "height", "The current block height (index)"}, + {RPCResult::Type::STR_HEX, "bestblock", "The hash of the block at the tip of the chain"}, + {RPCResult::Type::NUM, "transactions", "The number of transactions with unspent outputs"}, + {RPCResult::Type::NUM, "txouts", "The number of unspent transaction outputs"}, + {RPCResult::Type::NUM, "bogosize", "A meaningless metric for UTXO set size"}, + {RPCResult::Type::STR_HEX, "hash_serialized_2", "The serialized hash"}, + {RPCResult::Type::NUM, "disk_size", "The estimated size of the chainstate on disk"}, + {RPCResult::Type::STR_AMOUNT, "total_amount", "The total amount"}, + }}, RPCExamples{ HelpExampleCli("gettxoutsetinfo", "") + HelpExampleRpc("gettxoutsetinfo", "") @@ -1031,23 +1007,22 @@ UniValue gettxout(const JSONRPCRequest& request) {"include_mempool", RPCArg::Type::BOOL, /* default */ "true", "Whether to include the mempool. Note that an unspent output that is spent in the mempool won't appear."}, }, RPCResult{ - "{\n" - " \"bestblock\": \"hash\", (string) The hash of the block at the tip of the chain\n" - " \"confirmations\" : n, (numeric) The number of confirmations\n" - " \"value\" : x.xxx, (numeric) The transaction value in " + CURRENCY_UNIT + "\n" - " \"scriptPubKey\" : { (json object)\n" - " \"asm\" : \"code\", (string) \n" - " \"hex\" : \"hex\", (string) \n" - " \"reqSigs\" : n, (numeric) Number of required signatures\n" - " \"type\" : \"pubkeyhash\", (string) The type, eg pubkeyhash\n" - " \"addresses\" : [ (array of string) array of bitcoin addresses\n" - " \"address\" (string) bitcoin address\n" - " ,...\n" - " ]\n" - " },\n" - " \"coinbase\" : true|false (boolean) Coinbase or not\n" - "}\n" - }, + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "bestblock", "The hash of the block at the tip of the chain"}, + {RPCResult::Type::NUM, "confirmations", "The number of confirmations"}, + {RPCResult::Type::STR_AMOUNT, "value", "The transaction value in " + CURRENCY_UNIT}, + {RPCResult::Type::OBJ, "scriptPubKey", "", + { + {RPCResult::Type::STR_HEX, "asm", ""}, + {RPCResult::Type::STR_HEX, "hex", ""}, + {RPCResult::Type::NUM, "reqSigs", "Number of required signatures"}, + {RPCResult::Type::STR_HEX, "type", "The type, eg pubkeyhash"}, + {RPCResult::Type::ARR, "addresses", "array of bitcoin addresses", + {{RPCResult::Type::STR, "address", "bitcoin address"}}}, + }}, + {RPCResult::Type::BOOL, "coinbase", "Coinbase or not"}, + }}, RPCExamples{ "\nGet unspent transactions\n" + HelpExampleCli("listunspent", "") + @@ -1112,8 +1087,7 @@ static UniValue verifychain(const JSONRPCRequest& request) {"nblocks", RPCArg::Type::NUM, /* default */ strprintf("%d, 0=all", nCheckDepth), "The number of blocks to check."}, }, RPCResult{ - "true|false (boolean) Verified or not\n" - }, + RPCResult::Type::BOOL, "", "Verified or not"}, RPCExamples{ HelpExampleCli("verifychain", "") + HelpExampleRpc("verifychain", "") @@ -1203,45 +1177,49 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) "Returns an object containing various state info regarding blockchain processing.\n", {}, RPCResult{ - "{\n" - " \"chain\": \"xxxx\", (string) current network name (main, test, regtest)\n" - " \"blocks\": xxxxxx, (numeric) the height of the most-work fully-validated chain. The genesis block has height 0\n" - " \"headers\": xxxxxx, (numeric) the current number of headers we have validated\n" - " \"bestblockhash\": \"...\", (string) the hash of the currently best block\n" - " \"difficulty\": xxxxxx, (numeric) the current difficulty\n" - " \"mediantime\": xxxxxx, (numeric) median time for the current best block\n" - " \"verificationprogress\": xxxx, (numeric) estimate of verification progress [0..1]\n" - " \"initialblockdownload\": xxxx, (bool) (debug information) estimate of whether this node is in Initial Block Download mode.\n" - " \"chainwork\": \"xxxx\" (string) total amount of work in active chain, in hexadecimal\n" - " \"size_on_disk\": xxxxxx, (numeric) the estimated size of the block and undo files on disk\n" - " \"pruned\": xx, (boolean) if the blocks are subject to pruning\n" - " \"pruneheight\": xxxxxx, (numeric) lowest-height complete block stored (only present if pruning is enabled)\n" - " \"automatic_pruning\": xx, (boolean) whether automatic pruning is enabled (only present if pruning is enabled)\n" - " \"prune_target_size\": xxxxxx, (numeric) the target size used by pruning (only present if automatic pruning is enabled)\n" - " \"softforks\": { (object) status of softforks\n" - " \"xxxx\" : { (string) name of the softfork\n" - " \"type\": \"xxxx\", (string) one of \"buried\", \"bip9\"\n" - " \"bip9\": { (object) status of bip9 softforks (only for \"bip9\" type)\n" - " \"status\": \"xxxx\", (string) one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\"\n" - " \"bit\": xx, (numeric) the bit (0-28) in the block version field used to signal this softfork (only for \"started\" status)\n" - " \"start_time\": xx, (numeric) the minimum median time past of a block at which the bit gains its meaning\n" - " \"timeout\": xx, (numeric) the median time past of a block at which the deployment is considered failed if not yet locked in\n" - " \"since\": xx, (numeric) height of the first block to which the status applies\n" - " \"statistics\": { (object) numeric statistics about BIP9 signalling for a softfork\n" - " \"period\": xx, (numeric) the length in blocks of the BIP9 signalling period \n" - " \"threshold\": xx, (numeric) the number of blocks with the version bit set required to activate the feature \n" - " \"elapsed\": xx, (numeric) the number of blocks elapsed since the beginning of the current period \n" - " \"count\": xx, (numeric) the number of blocks with the version bit set in the current period \n" - " \"possible\": xx (boolean) returns false if there are not enough blocks left in this period to pass activation threshold \n" - " }\n" - " },\n" - " \"height\": \"xxxxxx\", (numeric) height of the first block which the rules are or will be enforced (only for \"buried\" type, or \"bip9\" type with \"active\" status)\n" - " \"active\": xx, (boolean) true if the rules are enforced for the mempool and the next block\n" - " }\n" - " }\n" - " \"warnings\" : \"...\", (string) any network and blockchain warnings.\n" - "}\n" - }, + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR, "chain", "current network name (main, test, regtest)"}, + {RPCResult::Type::NUM, "blocks", "the height of the most-work fully-validated chain. The genesis block has height 0"}, + {RPCResult::Type::NUM, "headers", "the current number of headers we have validated"}, + {RPCResult::Type::STR, "bestblockhash", "the hash of the currently best block"}, + {RPCResult::Type::NUM, "difficulty", "the current difficulty"}, + {RPCResult::Type::NUM, "mediantime", "median time for the current best block"}, + {RPCResult::Type::NUM, "verificationprogress", "estimate of verification progress [0..1]"}, + {RPCResult::Type::BOOL, "initialblockdownload", "(debug information) estimate of whether this node is in Initial Block Download mode"}, + {RPCResult::Type::STR_HEX, "chainwork", "total amount of work in active chain, in hexadecimal"}, + {RPCResult::Type::NUM, "size_on_disk", "the estimated size of the block and undo files on disk"}, + {RPCResult::Type::BOOL, "pruned", "if the blocks are subject to pruning"}, + {RPCResult::Type::NUM, "pruneheight", "lowest-height complete block stored (only present if pruning is enabled)"}, + {RPCResult::Type::BOOL, "automatic_pruning", "whether automatic pruning is enabled (only present if pruning is enabled)"}, + {RPCResult::Type::NUM, "prune_target_size", "the target size used by pruning (only present if automatic pruning is enabled)"}, + {RPCResult::Type::OBJ_DYN, "softforks", "status of softforks", + { + {RPCResult::Type::OBJ, "xxxx", "name of the softfork", + { + {RPCResult::Type::STR, "type", "one of \"buried\", \"bip9\""}, + {RPCResult::Type::OBJ, "bip9", "status of bip9 softforks (only for \"bip9\" type)", + { + {RPCResult::Type::STR, "status", "one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\""}, + {RPCResult::Type::NUM, "bit", "the bit (0-28) in the block version field used to signal this softfork (only for \"started\" status)"}, + {RPCResult::Type::NUM_TIME, "start_time", "the minimum median time past of a block at which the bit gains its meaning"}, + {RPCResult::Type::NUM_TIME, "timeout", "the median time past of a block at which the deployment is considered failed if not yet locked in"}, + {RPCResult::Type::NUM, "since", "height of the first block to which the status applies"}, + {RPCResult::Type::OBJ, "statistics", "numeric statistics about BIP9 signalling for a softfork", + { + {RPCResult::Type::NUM, "period", "the length in blocks of the BIP9 signalling period"}, + {RPCResult::Type::NUM, "threshold", "the number of blocks with the version bit set required to activate the feature"}, + {RPCResult::Type::NUM, "elapsed", "the number of blocks elapsed since the beginning of the current period"}, + {RPCResult::Type::NUM, "count", "the number of blocks with the version bit set in the current period"}, + {RPCResult::Type::BOOL, "possible", "returns false if there are not enough blocks left in this period to pass activation threshold"}, + }}, + }}, + {RPCResult::Type::NUM, "height", "height of the first block which the rules are or will be enforced (only for \"buried\" type, or \"bip9\" type with \"active\" status)"}, + {RPCResult::Type::BOOL, "active", "true if the rules are enforced for the mempool and the next block"}, + }}, + }}, + {RPCResult::Type::STR, "warnings", "any network and blockchain warnings"}, + }}, RPCExamples{ HelpExampleCli("getblockchaininfo", "") + HelpExampleRpc("getblockchaininfo", "") @@ -1316,27 +1294,20 @@ static UniValue getchaintips(const JSONRPCRequest& request) " including the main chain as well as orphaned branches.\n", {}, RPCResult{ - "[\n" - " {\n" - " \"height\": xxxx, (numeric) height of the chain tip\n" - " \"hash\": \"xxxx\", (string) block hash of the tip\n" - " \"branchlen\": 0 (numeric) zero for main chain\n" - " \"status\": \"active\" (string) \"active\" for the main chain\n" - " },\n" - " {\n" - " \"height\": xxxx,\n" - " \"hash\": \"xxxx\",\n" - " \"branchlen\": 1 (numeric) length of branch connecting the tip to the main chain\n" - " \"status\": \"xxxx\" (string) status of the chain (active, valid-fork, valid-headers, headers-only, invalid)\n" - " }\n" - "]\n" + RPCResult::Type::ARR, "", "", + {{RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::NUM, "height", "height of the chain tip"}, + {RPCResult::Type::STR_HEX, "hash", "block hash of the tip"}, + {RPCResult::Type::NUM, "branchlen", "zero for main chain, otherwise length of branch connecting the tip to the main chain"}, + {RPCResult::Type::STR, "status", "status of the chain, \"active\" for the main chain\n" "Possible values for status:\n" "1. \"invalid\" This branch contains at least one invalid block\n" "2. \"headers-only\" Not all blocks for this branch are available, but the headers are valid\n" "3. \"valid-headers\" All blocks are available for this branch, but they were never fully validated\n" "4. \"valid-fork\" This branch is not part of the active chain, but is fully validated\n" - "5. \"active\" This is the tip of the active main chain, which is certainly valid\n" - }, + "5. \"active\" This is the tip of the active main chain, which is certainly valid"}, + }}}}, RPCExamples{ HelpExampleCli("getchaintips", "") + HelpExampleRpc("getchaintips", "") @@ -1436,16 +1407,16 @@ static UniValue getmempoolinfo(const JSONRPCRequest& request) "\nReturns details on the active state of the TX memory pool.\n", {}, RPCResult{ - "{\n" - " \"loaded\": true|false (boolean) True if the mempool is fully loaded\n" - " \"size\": xxxxx, (numeric) Current tx count\n" - " \"bytes\": xxxxx, (numeric) Sum of all virtual transaction sizes as defined in BIP 141. Differs from actual serialized size because witness data is discounted\n" - " \"usage\": xxxxx, (numeric) Total memory usage for the mempool\n" - " \"maxmempool\": xxxxx, (numeric) Maximum memory usage for the mempool\n" - " \"mempoolminfee\": xxxxx (numeric) Minimum fee rate in " + CURRENCY_UNIT + "/kB for tx to be accepted. Is the maximum of minrelaytxfee and minimum mempool fee\n" - " \"minrelaytxfee\": xxxxx (numeric) Current minimum relay fee for transactions\n" - "}\n" - }, + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::BOOL, "loaded", "True if the mempool is fully loaded"}, + {RPCResult::Type::NUM, "size", "Current tx count"}, + {RPCResult::Type::NUM, "bytes", "Sum of all virtual transaction sizes as defined in BIP 141. Differs from actual serialized size because witness data is discounted"}, + {RPCResult::Type::NUM, "usage", "Total memory usage for the mempool"}, + {RPCResult::Type::NUM, "maxmempool", "Maximum memory usage for the mempool"}, + {RPCResult::Type::STR_AMOUNT, "mempoolminfee", "Minimum fee rate in " + CURRENCY_UNIT + "/kB for tx to be accepted. Is the maximum of minrelaytxfee and minimum mempool fee"}, + {RPCResult::Type::STR_AMOUNT, "minrelaytxfee", "Current minimum relay fee for transactions"}, + }}, RPCExamples{ HelpExampleCli("getmempoolinfo", "") + HelpExampleRpc("getmempoolinfo", "") @@ -1486,7 +1457,7 @@ static UniValue preciousblock(const JSONRPCRequest& request) PreciousBlock(state, Params(), pblockindex); if (!state.IsValid()) { - throw JSONRPCError(RPC_DATABASE_ERROR, FormatStateMessage(state)); + throw JSONRPCError(RPC_DATABASE_ERROR, state.ToString()); } return NullUniValue; @@ -1524,7 +1495,7 @@ static UniValue invalidateblock(const JSONRPCRequest& request) } if (!state.IsValid()) { - throw JSONRPCError(RPC_DATABASE_ERROR, FormatStateMessage(state)); + throw JSONRPCError(RPC_DATABASE_ERROR, state.ToString()); } return NullUniValue; @@ -1561,7 +1532,7 @@ static UniValue reconsiderblock(const JSONRPCRequest& request) ActivateBestChain(state, Params()); if (!state.IsValid()) { - throw JSONRPCError(RPC_DATABASE_ERROR, FormatStateMessage(state)); + throw JSONRPCError(RPC_DATABASE_ERROR, state.ToString()); } return NullUniValue; @@ -1576,17 +1547,17 @@ static UniValue getchaintxstats(const JSONRPCRequest& request) {"blockhash", RPCArg::Type::STR_HEX, /* default */ "chain tip", "The hash of the block that ends the window."}, }, RPCResult{ - "{\n" - " \"time\": xxxxx, (numeric) The timestamp for the final block in the window, expressed in " + UNIX_EPOCH_TIME + ".\n" - " \"txcount\": xxxxx, (numeric) The total number of transactions in the chain up to that point.\n" - " \"window_final_block_hash\": \"...\", (string) The hash of the final block in the window.\n" - " \"window_final_block_height\": xxxxx, (numeric) The height of the final block in the window.\n" - " \"window_block_count\": xxxxx, (numeric) Size of the window in number of blocks.\n" - " \"window_tx_count\": xxxxx, (numeric) The number of transactions in the window. Only returned if \"window_block_count\" is > 0.\n" - " \"window_interval\": xxxxx, (numeric) The elapsed time in the window in seconds. Only returned if \"window_block_count\" is > 0.\n" - " \"txrate\": x.xx, (numeric) The average rate of transactions per second in the window. Only returned if \"window_interval\" is > 0.\n" - "}\n" - }, + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::NUM_TIME, "time", "The timestamp for the final block in the window, expressed in " + UNIX_EPOCH_TIME}, + {RPCResult::Type::NUM, "txcount", "The total number of transactions in the chain up to that point"}, + {RPCResult::Type::STR_HEX, "window_final_block_hash", "The hash of the final block in the window"}, + {RPCResult::Type::NUM, "window_final_block_height", "The height of the final block in the window."}, + {RPCResult::Type::NUM, "window_block_count", "Size of the window in number of blocks"}, + {RPCResult::Type::NUM, "window_tx_count", "The number of transactions in the window. Only returned if \"window_block_count\" is > 0"}, + {RPCResult::Type::NUM, "window_interval", "The elapsed time in the window in seconds. Only returned if \"window_block_count\" is > 0"}, + {RPCResult::Type::NUM, "txrate", "The average rate of transactions per second in the window. Only returned if \"window_interval\" is > 0"}, + }}, RPCExamples{ HelpExampleCli("getchaintxstats", "") + HelpExampleRpc("getchaintxstats", "2016") @@ -1715,44 +1686,45 @@ static UniValue getblockstats(const JSONRPCRequest& request) "stats"}, }, RPCResult{ - "{ (json object)\n" - " \"avgfee\": xxxxx, (numeric) Average fee in the block\n" - " \"avgfeerate\": xxxxx, (numeric) Average feerate (in satoshis per virtual byte)\n" - " \"avgtxsize\": xxxxx, (numeric) Average transaction size\n" - " \"blockhash\": xxxxx, (string) The block hash (to check for potential reorgs)\n" - " \"feerate_percentiles\": [ (array of numeric) Feerates at the 10th, 25th, 50th, 75th, and 90th percentile weight unit (in satoshis per virtual byte)\n" - " \"10th_percentile_feerate\", (numeric) The 10th percentile feerate\n" - " \"25th_percentile_feerate\", (numeric) The 25th percentile feerate\n" - " \"50th_percentile_feerate\", (numeric) The 50th percentile feerate\n" - " \"75th_percentile_feerate\", (numeric) The 75th percentile feerate\n" - " \"90th_percentile_feerate\", (numeric) The 90th percentile feerate\n" - " ],\n" - " \"height\": xxxxx, (numeric) The height of the block\n" - " \"ins\": xxxxx, (numeric) The number of inputs (excluding coinbase)\n" - " \"maxfee\": xxxxx, (numeric) Maximum fee in the block\n" - " \"maxfeerate\": xxxxx, (numeric) Maximum feerate (in satoshis per virtual byte)\n" - " \"maxtxsize\": xxxxx, (numeric) Maximum transaction size\n" - " \"medianfee\": xxxxx, (numeric) Truncated median fee in the block\n" - " \"mediantime\": xxxxx, (numeric) The block median time past\n" - " \"mediantxsize\": xxxxx, (numeric) Truncated median transaction size\n" - " \"minfee\": xxxxx, (numeric) Minimum fee in the block\n" - " \"minfeerate\": xxxxx, (numeric) Minimum feerate (in satoshis per virtual byte)\n" - " \"mintxsize\": xxxxx, (numeric) Minimum transaction size\n" - " \"outs\": xxxxx, (numeric) The number of outputs\n" - " \"subsidy\": xxxxx, (numeric) The block subsidy\n" - " \"swtotal_size\": xxxxx, (numeric) Total size of all segwit transactions\n" - " \"swtotal_weight\": xxxxx, (numeric) Total weight of all segwit transactions divided by segwit scale factor (4)\n" - " \"swtxs\": xxxxx, (numeric) The number of segwit transactions\n" - " \"time\": xxxxx, (numeric) The block time\n" - " \"total_out\": xxxxx, (numeric) Total amount in all outputs (excluding coinbase and thus reward [ie subsidy + totalfee])\n" - " \"total_size\": xxxxx, (numeric) Total size of all non-coinbase transactions\n" - " \"total_weight\": xxxxx, (numeric) Total weight of all non-coinbase transactions divided by segwit scale factor (4)\n" - " \"totalfee\": xxxxx, (numeric) The fee total\n" - " \"txs\": xxxxx, (numeric) The number of transactions (excluding coinbase)\n" - " \"utxo_increase\": xxxxx, (numeric) The increase/decrease in the number of unspent outputs\n" - " \"utxo_size_inc\": xxxxx, (numeric) The increase/decrease in size for the utxo index (not discounting op_return and similar)\n" - "}\n" - }, + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::NUM, "avgfee", "Average fee in the block"}, + {RPCResult::Type::NUM, "avgfeerate", "Average feerate (in satoshis per virtual byte)"}, + {RPCResult::Type::NUM, "avgtxsize", "Average transaction size"}, + {RPCResult::Type::STR_HEX, "blockhash", "The block hash (to check for potential reorgs)"}, + {RPCResult::Type::ARR_FIXED, "feerate_percentiles", "Feerates at the 10th, 25th, 50th, 75th, and 90th percentile weight unit (in satoshis per virtual byte)", + { + {RPCResult::Type::NUM, "10th_percentile_feerate", "The 10th percentile feerate"}, + {RPCResult::Type::NUM, "25th_percentile_feerate", "The 25th percentile feerate"}, + {RPCResult::Type::NUM, "50th_percentile_feerate", "The 50th percentile feerate"}, + {RPCResult::Type::NUM, "75th_percentile_feerate", "The 75th percentile feerate"}, + {RPCResult::Type::NUM, "90th_percentile_feerate", "The 90th percentile feerate"}, + }}, + {RPCResult::Type::NUM, "height", "The height of the block"}, + {RPCResult::Type::NUM, "ins", "The number of inputs (excluding coinbase)"}, + {RPCResult::Type::NUM, "maxfee", "Maximum fee in the block"}, + {RPCResult::Type::NUM, "maxfeerate", "Maximum feerate (in satoshis per virtual byte)"}, + {RPCResult::Type::NUM, "maxtxsize", "Maximum transaction size"}, + {RPCResult::Type::NUM, "medianfee", "Truncated median fee in the block"}, + {RPCResult::Type::NUM, "mediantime", "The block median time past"}, + {RPCResult::Type::NUM, "mediantxsize", "Truncated median transaction size"}, + {RPCResult::Type::NUM, "minfee", "Minimum fee in the block"}, + {RPCResult::Type::NUM, "minfeerate", "Minimum feerate (in satoshis per virtual byte)"}, + {RPCResult::Type::NUM, "mintxsize", "Minimum transaction size"}, + {RPCResult::Type::NUM, "outs", "The number of outputs"}, + {RPCResult::Type::NUM, "subsidy", "The block subsidy"}, + {RPCResult::Type::NUM, "swtotal_size", "Total size of all segwit transactions"}, + {RPCResult::Type::NUM, "swtotal_weight", "Total weight of all segwit transactions divided by segwit scale factor (4)"}, + {RPCResult::Type::NUM, "swtxs", "The number of segwit transactions"}, + {RPCResult::Type::NUM, "time", "The block time"}, + {RPCResult::Type::NUM, "total_out", "Total amount in all outputs (excluding coinbase and thus reward [ie subsidy + totalfee])"}, + {RPCResult::Type::NUM, "total_size", "Total size of all non-coinbase transactions"}, + {RPCResult::Type::NUM, "total_weight", "Total weight of all non-coinbase transactions divided by segwit scale factor (4)"}, + {RPCResult::Type::NUM, "totalfee", "The fee total"}, + {RPCResult::Type::NUM, "txs", "The number of transactions (excluding coinbase)"}, + {RPCResult::Type::NUM, "utxo_increase", "The increase/decrease in the number of unspent outputs"}, + {RPCResult::Type::NUM, "utxo_size_inc", "The increase/decrease in size for the utxo index (not discounting op_return and similar)"}, + }}, RPCExamples{ HelpExampleCli("getblockstats", "1000 '[\"minfeerate\",\"avgfeerate\"]'") + HelpExampleRpc("getblockstats", "1000 '[\"minfeerate\",\"avgfeerate\"]'") @@ -2075,24 +2047,26 @@ UniValue scantxoutset(const JSONRPCRequest& request) "[scanobjects,...]"}, }, RPCResult{ - "{\n" - " \"success\": true|false, (boolean) Whether the scan was completed\n" - " \"txouts\": n, (numeric) The number of unspent transaction outputs scanned\n" - " \"height\": n, (numeric) The current block height (index)\n" - " \"bestblock\": \"hex\", (string) The hash of the block at the tip of the chain\n" - " \"unspents\": [\n" - " {\n" - " \"txid\": \"hash\", (string) The transaction id\n" - " \"vout\": n, (numeric) The vout value\n" - " \"scriptPubKey\": \"script\", (string) The script key\n" - " \"desc\": \"descriptor\", (string) A specialized descriptor for the matched scriptPubKey\n" - " \"amount\": x.xxx, (numeric) The total amount in " + CURRENCY_UNIT + " of the unspent output\n" - " \"height\": n, (numeric) Height of the unspent transaction output\n" - " }\n" - " ,...],\n" - " \"total_amount\": x.xxx, (numeric) The total amount of all found unspent outputs in " + CURRENCY_UNIT + "\n" - "]\n" - }, + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::BOOL, "success", "Whether the scan was completed"}, + {RPCResult::Type::NUM, "txouts", "The number of unspent transaction outputs scanned"}, + {RPCResult::Type::NUM, "height", "The current block height (index)"}, + {RPCResult::Type::STR_HEX, "bestblock", "The hash of the block at the tip of the chain"}, + {RPCResult::Type::ARR, "unspents", "", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "txid", "The transaction id"}, + {RPCResult::Type::NUM, "vout", "The vout value"}, + {RPCResult::Type::STR_HEX, "scriptPubKey", "The script key"}, + {RPCResult::Type::STR, "desc", "A specialized descriptor for the matched scriptPubKey"}, + {RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " of the unspent output"}, + {RPCResult::Type::NUM, "height", "Height of the unspent transaction output"}, + }}, + }}, + {RPCResult::Type::STR_AMOUNT, "total_amount", "The total amount of all found unspent outputs in " + CURRENCY_UNIT}, + }}, RPCExamples{""}, }.Check(request); @@ -2198,11 +2172,11 @@ static UniValue getblockfilter(const JSONRPCRequest& request) {"filtertype", RPCArg::Type::STR, /*default*/ "basic", "The type name of the filter"}, }, RPCResult{ - "{\n" - " \"filter\" : (string) the hex-encoded filter data\n" - " \"header\" : (string) the hex-encoded filter header\n" - "}\n" - }, + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "filter", "the hex-encoded filter data"}, + {RPCResult::Type::STR_HEX, "header", "the hex-encoded filter header"}, + }}, RPCExamples{ HelpExampleCli("getblockfilter", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\" \"basic\"") } @@ -2283,12 +2257,13 @@ UniValue dumptxoutset(const JSONRPCRequest& request) "path to the output file. If relative, will be prefixed by datadir."}, }, RPCResult{ - "{\n" - " \"coins_written\": n, (numeric) the number of coins written in the snapshot\n" - " \"base_hash\": \"...\", (string) the hash of the base of the snapshot\n" - " \"base_height\": n, (string) the height of the base of the snapshot\n" - " \"path\": \"...\" (string) the absolute path that the snapshot was written to\n" - "]\n" + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::NUM, "coins_written", "the number of coins written in the snapshot"}, + {RPCResult::Type::STR_HEX, "base_hash", "the hash of the base of the snapshot"}, + {RPCResult::Type::NUM, "base_height", "the height of the base of the snapshot"}, + {RPCResult::Type::STR, "path", "the absolute path that the snapshot was written to"}, + } }, RPCExamples{ HelpExampleCli("dumptxoutset", "utxo.dat") diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 2eaa3427eb..c1762483e9 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -27,6 +27,7 @@ public: static const CRPCConvertParam vRPCConvertParams[] = { { "setmocktime", 0, "timestamp" }, + { "mockscheduler", 0, "delta_time" }, { "utxoupdatepsbt", 1, "descriptors" }, { "generatetoaddress", 0, "nblocks" }, { "generatetoaddress", 2, "maxtries" }, diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 58c5ce32ce..92440488a4 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -28,7 +28,6 @@ #include <util/fees.h> #include <util/strencodings.h> #include <util/system.h> -#include <util/validation.h> #include <validation.h> #include <validationinterface.h> #include <versionbitsinfo.h> @@ -90,8 +89,7 @@ static UniValue getnetworkhashps(const JSONRPCRequest& request) {"height", RPCArg::Type::NUM, /* default */ "-1", "To estimate at the time of the given height."}, }, RPCResult{ - "x (numeric) Hashes per second estimated\n" - }, + RPCResult::Type::NUM, "", "Hashes per second estimated"}, RPCExamples{ HelpExampleCli("getnetworkhashps", "") + HelpExampleRpc("getnetworkhashps", "") @@ -154,7 +152,11 @@ static UniValue generatetodescriptor(const JSONRPCRequest& request) {"maxtries", RPCArg::Type::NUM, /* default */ "1000000", "How many iterations to try."}, }, RPCResult{ - "[ blockhashes ] (array) hashes of blocks generated\n"}, + RPCResult::Type::ARR, "", "hashes of blocks generated", + { + {RPCResult::Type::STR_HEX, "", "blockhash"}, + } + }, RPCExamples{ "\nGenerate 11 blocks to mydesc\n" + HelpExampleCli("generatetodescriptor", "11 \"mydesc\"")}, } @@ -196,8 +198,10 @@ static UniValue generatetoaddress(const JSONRPCRequest& request) {"maxtries", RPCArg::Type::NUM, /* default */ "1000000", "How many iterations to try."}, }, RPCResult{ - "[ blockhashes ] (array) hashes of blocks generated\n" - }, + RPCResult::Type::ARR, "", "hashes of blocks generated", + { + {RPCResult::Type::STR_HEX, "", "blockhash"}, + }}, RPCExamples{ "\nGenerate 11 blocks to myaddress\n" + HelpExampleCli("generatetoaddress", "11 \"myaddress\"") @@ -230,17 +234,17 @@ static UniValue getmininginfo(const JSONRPCRequest& request) "\nReturns a json object containing mining-related information.", {}, RPCResult{ - "{\n" - " \"blocks\": nnn, (numeric) The current block\n" - " \"currentblockweight\": nnn, (numeric, optional) The block weight of the last assembled block (only present if a block was ever assembled)\n" - " \"currentblocktx\": nnn, (numeric, optional) The number of block transactions of the last assembled block (only present if a block was ever assembled)\n" - " \"difficulty\": xxx.xxxxx (numeric) The current difficulty\n" - " \"networkhashps\": nnn, (numeric) The network hashes per second\n" - " \"pooledtx\": n (numeric) The size of the mempool\n" - " \"chain\": \"xxxx\", (string) current network name (main, test, regtest)\n" - " \"warnings\": \"...\" (string) any network and blockchain warnings\n" - "}\n" - }, + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::NUM, "blocks", "The current block"}, + {RPCResult::Type::NUM, "currentblockweight", /* optional */ true, "The block weight of the last assembled block (only present if a block was ever assembled)"}, + {RPCResult::Type::NUM, "currentblocktx", /* optional */ true, "The number of block transactions of the last assembled block (only present if a block was ever assembled)"}, + {RPCResult::Type::NUM, "difficulty", "The current difficulty"}, + {RPCResult::Type::NUM, "networkhashps", "The network hashes per second"}, + {RPCResult::Type::NUM, "pooledtx", "The size of the mempool"}, + {RPCResult::Type::STR, "chain", "current network name (main, test, regtest)"}, + {RPCResult::Type::STR, "warnings", "any network and blockchain warnings"}, + }}, RPCExamples{ HelpExampleCli("getmininginfo", "") + HelpExampleRpc("getmininginfo", "") @@ -278,8 +282,7 @@ static UniValue prioritisetransaction(const JSONRPCRequest& request) " considers the transaction as it would have paid a higher (or lower) fee."}, }, RPCResult{ - "true (boolean) Returns true\n" - }, + RPCResult::Type::BOOL, "", "Returns true"}, RPCExamples{ HelpExampleCli("prioritisetransaction", "\"txid\" 0.0 10000") + HelpExampleRpc("prioritisetransaction", "\"txid\", 0.0, 10000") @@ -307,7 +310,7 @@ static UniValue BIP22ValidationResult(const BlockValidationState& state) return NullUniValue; if (state.IsError()) - throw JSONRPCError(RPC_VERIFY_ERROR, FormatStateMessage(state)); + throw JSONRPCError(RPC_VERIFY_ERROR, state.ToString()); if (state.IsInvalid()) { std::string strRejectReason = state.GetRejectReason(); @@ -356,48 +359,58 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) "\"template_request\""}, }, RPCResult{ - "{\n" - " \"version\" : n, (numeric) The preferred block version\n" - " \"rules\" : [ \"rulename\", ... ], (array of strings) specific block rules that are to be enforced\n" - " \"vbavailable\" : { (json object) set of pending, supported versionbit (BIP 9) softfork deployments\n" - " \"rulename\" : bitnumber (numeric) identifies the bit number as indicating acceptance and readiness for the named softfork rule\n" - " ,...\n" - " },\n" - " \"vbrequired\" : n, (numeric) bit mask of versionbits the server requires set in submissions\n" - " \"previousblockhash\" : \"xxxx\", (string) The hash of current highest block\n" - " \"transactions\" : [ (array) contents of non-coinbase transactions that should be included in the next block\n" - " {\n" - " \"data\" : \"xxxx\", (string) transaction data encoded in hexadecimal (byte-for-byte)\n" - " \"txid\" : \"xxxx\", (string) transaction id encoded in little-endian hexadecimal\n" - " \"hash\" : \"xxxx\", (string) hash encoded in little-endian hexadecimal (including witness data)\n" - " \"depends\" : [ (array) array of numbers \n" - " n (numeric) transactions before this one (by 1-based index in 'transactions' list) that must be present in the final block if this one is\n" - " ,...\n" - " ],\n" - " \"fee\": n, (numeric) difference in value between transaction inputs and outputs (in satoshis); for coinbase transactions, this is a negative Number of the total collected block fees (ie, not including the block subsidy); if key is not present, fee is unknown and clients MUST NOT assume there isn't one\n" - " \"sigops\" : n, (numeric) total SigOps cost, as counted for purposes of block limits; if key is not present, sigop cost is unknown and clients MUST NOT assume it is zero\n" - " \"weight\" : n, (numeric) total transaction weight, as counted for purposes of block limits\n" - " }\n" - " ,...\n" - " ],\n" - " \"coinbaseaux\" : { ... }, (json object) data that should be included in the coinbase's scriptSig content\n" - " \"coinbasevalue\" : n, (numeric) maximum allowable input to coinbase transaction, including the generation award and transaction fees (in satoshis)\n" - " \"coinbasetxn\" : { ... }, (json object) information for coinbase transaction\n" - " \"target\" : \"xxxx\", (string) The hash target\n" - " \"mintime\" : xxx, (numeric) The minimum timestamp appropriate for the next block time, expressed in " + UNIX_EPOCH_TIME + "\n" - " \"mutable\" : [ (array of string) list of ways the block template may be changed \n" - " \"value\" (string) A way the block template may be changed, e.g. 'time', 'transactions', 'prevblock'\n" - " ,...\n" - " ],\n" - " \"noncerange\" : \"00000000ffffffff\",(string) A range of valid nonces\n" - " \"sigoplimit\" : n, (numeric) limit of sigops in blocks\n" - " \"sizelimit\" : n, (numeric) limit of block size\n" - " \"weightlimit\" : n, (numeric) limit of block weight\n" - " \"curtime\" : ttt, (numeric) current timestamp in " + UNIX_EPOCH_TIME + "\n" - " \"bits\" : \"xxxxxxxx\", (string) compressed target of next block\n" - " \"height\" : n (numeric) The height of the next block\n" - "}\n" - }, + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::NUM, "version", "The preferred block version"}, + {RPCResult::Type::ARR, "rules", "specific block rules that are to be enforced", + { + {RPCResult::Type::STR, "", "rulename"}, + }}, + {RPCResult::Type::OBJ_DYN, "vbavailable", "set of pending, supported versionbit (BIP 9) softfork deployments", + { + {RPCResult::Type::NUM, "rulename", "identifies the bit number as indicating acceptance and readiness for the named softfork rule"}, + }}, + {RPCResult::Type::NUM, "vbrequired", "bit mask of versionbits the server requires set in submissions"}, + {RPCResult::Type::STR, "previousblockhash", "The hash of current highest block"}, + {RPCResult::Type::ARR, "", "contents of non-coinbase transactions that should be included in the next block", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "data", "transaction data encoded in hexadecimal (byte-for-byte)"}, + {RPCResult::Type::STR_HEX, "txid", "transaction id encoded in little-endian hexadecimal"}, + {RPCResult::Type::STR_HEX, "hash", "hash encoded in little-endian hexadecimal (including witness data)"}, + {RPCResult::Type::ARR, "depends", "array of numbers", + { + {RPCResult::Type::NUM, "", "transactions before this one (by 1-based index in 'transactions' list) that must be present in the final block if this one is"}, + }}, + {RPCResult::Type::NUM, "fee", "difference in value between transaction inputs and outputs (in satoshis); for coinbase transactions, this is a negative Number of the total collected block fees (ie, not including the block subsidy); if key is not present, fee is unknown and clients MUST NOT assume there isn't one"}, + {RPCResult::Type::NUM, "sigops", "total SigOps cost, as counted for purposes of block limits; if key is not present, sigop cost is unknown and clients MUST NOT assume it is zero"}, + {RPCResult::Type::NUM, "weight", "total transaction weight, as counted for purposes of block limits"}, + }}, + }}, + {RPCResult::Type::OBJ, "coinbaseaux", "data that should be included in the coinbase's scriptSig content", + { + {RPCResult::Type::ELISION, "", ""}, + }}, + {RPCResult::Type::NUM, "coinbasevalue", "maximum allowable input to coinbase transaction, including the generation award and transaction fees (in satoshis)"}, + {RPCResult::Type::OBJ, "coinbasetxn", "information for coinbase transaction", + { + {RPCResult::Type::ELISION, "", ""}, + }}, + {RPCResult::Type::STR, "target", "The hash target"}, + {RPCResult::Type::NUM_TIME, "mintime", "The minimum timestamp appropriate for the next block time, expressed in " + UNIX_EPOCH_TIME}, + {RPCResult::Type::ARR, "mutable", "list of ways the block template may be changed", + { + {RPCResult::Type::STR, "value", "A way the block template may be changed, e.g. 'time', 'transactions', 'prevblock'"}, + }}, + {RPCResult::Type::STR_HEX, "noncerange", "A range of valid nonces"}, + {RPCResult::Type::NUM, "sigoplimit", "limit of sigops in blocks"}, + {RPCResult::Type::NUM, "sizelimit", "limit of block size"}, + {RPCResult::Type::NUM, "weightlimit", "limit of block weight"}, + {RPCResult::Type::NUM_TIME, "curtime", "current timestamp in " + UNIX_EPOCH_TIME}, + {RPCResult::Type::STR, "bits", "compressed target of next block"}, + {RPCResult::Type::NUM, "height", "The height of the next block"}, + }}, RPCExamples{ HelpExampleCli("getblocktemplate", "'{\"rules\": [\"segwit\"]}'") + HelpExampleRpc("getblocktemplate", "{\"rules\": [\"segwit\"]}") @@ -800,8 +813,7 @@ static UniValue submitheader(const JSONRPCRequest& request) {"hexdata", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "the hex-encoded block header data"}, }, RPCResult{ - "None" - }, + RPCResult::Type::NONE, "", "None"}, RPCExamples{ HelpExampleCli("submitheader", "\"aabbcc\"") + HelpExampleRpc("submitheader", "\"aabbcc\"") @@ -823,7 +835,7 @@ static UniValue submitheader(const JSONRPCRequest& request) ProcessNewBlockHeaders({h}, state, Params()); if (state.IsValid()) return NullUniValue; if (state.IsError()) { - throw JSONRPCError(RPC_VERIFY_ERROR, FormatStateMessage(state)); + throw JSONRPCError(RPC_VERIFY_ERROR, state.ToString()); } throw JSONRPCError(RPC_VERIFY_ERROR, state.GetRejectReason()); } @@ -848,17 +860,19 @@ static UniValue estimatesmartfee(const JSONRPCRequest& request) " \"CONSERVATIVE\""}, }, RPCResult{ - "{\n" - " \"feerate\" : x.x, (numeric, optional) estimate fee rate in " + CURRENCY_UNIT + "/kB\n" - " \"errors\": [ str... ] (json array of strings, optional) Errors encountered during processing\n" - " \"blocks\" : n (numeric) block number where estimate was found\n" - "}\n" - "\n" + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::NUM, "feerate", /* optional */ true, "estimate fee rate in " + CURRENCY_UNIT + "/kB (only present if no errors were encountered)"}, + {RPCResult::Type::ARR, "errors", "Errors encountered during processing", + { + {RPCResult::Type::STR, "", "error"}, + }}, + {RPCResult::Type::NUM, "blocks", "block number where estimate was found\n" "The request target will be clamped between 2 and the highest target\n" "fee estimation is able to return based on how long it has been running.\n" "An error is returned if not enough transactions and blocks\n" - "have been observed to make an estimate for any number of blocks.\n" - }, + "have been observed to make an estimate for any number of blocks."}, + }}, RPCExamples{ HelpExampleCli("estimatesmartfee", "6") }, @@ -908,28 +922,40 @@ static UniValue estimaterawfee(const JSONRPCRequest& request) " lower buckets."}, }, RPCResult{ - "{\n" - " \"short\" : { (json object, optional) estimate for short time horizon\n" - " \"feerate\" : x.x, (numeric, optional) estimate fee rate in " + CURRENCY_UNIT + "/kB\n" - " \"decay\" : x.x, (numeric) exponential decay (per block) for historical moving average of confirmation data\n" - " \"scale\" : x, (numeric) The resolution of confirmation targets at this time horizon\n" - " \"pass\" : { (json object, optional) information about the lowest range of feerates to succeed in meeting the threshold\n" - " \"startrange\" : x.x, (numeric) start of feerate range\n" - " \"endrange\" : x.x, (numeric) end of feerate range\n" - " \"withintarget\" : x.x, (numeric) number of txs over history horizon in the feerate range that were confirmed within target\n" - " \"totalconfirmed\" : x.x, (numeric) number of txs over history horizon in the feerate range that were confirmed at any point\n" - " \"inmempool\" : x.x, (numeric) current number of txs in mempool in the feerate range unconfirmed for at least target blocks\n" - " \"leftmempool\" : x.x, (numeric) number of txs over history horizon in the feerate range that left mempool unconfirmed after target\n" - " },\n" - " \"fail\" : { ... }, (json object, optional) information about the highest range of feerates to fail to meet the threshold\n" - " \"errors\": [ str... ] (json array of strings, optional) Errors encountered during processing\n" - " },\n" - " \"medium\" : { ... }, (json object, optional) estimate for medium time horizon\n" - " \"long\" : { ... } (json object) estimate for long time horizon\n" - "}\n" - "\n" - "Results are returned for any horizon which tracks blocks up to the confirmation target.\n" - }, + RPCResult::Type::OBJ, "", "Results are returned for any horizon which tracks blocks up to the confirmation target", + { + {RPCResult::Type::OBJ, "short", /* optional */ true, "estimate for short time horizon", + { + {RPCResult::Type::NUM, "feerate", /* optional */ true, "estimate fee rate in " + CURRENCY_UNIT + "/kB"}, + {RPCResult::Type::NUM, "decay", "exponential decay (per block) for historical moving average of confirmation data"}, + {RPCResult::Type::NUM, "scale", "The resolution of confirmation targets at this time horizon"}, + {RPCResult::Type::OBJ, "pass", /* optional */ true, "information about the lowest range of feerates to succeed in meeting the threshold", + { + {RPCResult::Type::NUM, "startrange", "start of feerate range"}, + {RPCResult::Type::NUM, "endrange", "end of feerate range"}, + {RPCResult::Type::NUM, "withintarget", "number of txs over history horizon in the feerate range that were confirmed within target"}, + {RPCResult::Type::NUM, "totalconfirmed", "number of txs over history horizon in the feerate range that were confirmed at any point"}, + {RPCResult::Type::NUM, "inmempool", "current number of txs in mempool in the feerate range unconfirmed for at least target blocks"}, + {RPCResult::Type::NUM, "leftmempool", "number of txs over history horizon in the feerate range that left mempool unconfirmed after target"}, + }}, + {RPCResult::Type::OBJ, "fail", /* optional */ true, "information about the highest range of feerates to fail to meet the threshold", + { + {RPCResult::Type::ELISION, "", ""}, + }}, + {RPCResult::Type::ARR, "errors", /* optional */ true, "Errors encountered during processing", + { + {RPCResult::Type::STR, "error", ""}, + }}, + }}, + {RPCResult::Type::OBJ, "medium", /* optional */ true, "estimate for medium time horizon", + { + {RPCResult::Type::ELISION, "", ""}, + }}, + {RPCResult::Type::OBJ, "long", /* optional */ true, "estimate for long time horizon", + { + {RPCResult::Type::ELISION, "", ""}, + }}, + }}, RPCExamples{ HelpExampleCli("estimaterawfee", "6 0.9") }, diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index ad51452d06..4b16dd21a0 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -5,15 +5,17 @@ #include <httpserver.h> #include <key_io.h> +#include <node/context.h> #include <outputtype.h> #include <rpc/blockchain.h> #include <rpc/server.h> #include <rpc/util.h> +#include <scheduler.h> #include <script/descriptor.h> #include <util/check.h> +#include <util/message.h> // For MessageSign(), MessageVerify() #include <util/strencodings.h> #include <util/system.h> -#include <util/validation.h> #include <stdint.h> #include <tuple> @@ -31,19 +33,20 @@ static UniValue validateaddress(const JSONRPCRequest& request) {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address to validate"}, }, RPCResult{ - "{\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" - " \"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" - " \"witness_program\" : \"hex\" (string, optional) The hex value of the witness program\n" - "}\n" + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::BOOL, "isvalid", "If the address is valid or not. If not, this is the only property returned."}, + {RPCResult::Type::STR, "address", "The bitcoin address validated"}, + {RPCResult::Type::STR_HEX, "scriptPubKey", "The hex-encoded scriptPubKey generated by the address"}, + {RPCResult::Type::BOOL, "isscript", "If the key is a script"}, + {RPCResult::Type::BOOL, "iswitness", "If the address is a witness address"}, + {RPCResult::Type::NUM, "witness_version", /* optional */ true, "The version number of the witness program"}, + {RPCResult::Type::STR_HEX, "witness_program", /* optional */ true, "The hex value of the witness program"}, + } }, RPCExamples{ - HelpExampleCli("validateaddress", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"") - + HelpExampleRpc("validateaddress", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"") + HelpExampleCli("validateaddress", "\"" + EXAMPLE_ADDRESS[0] + "\"") + + HelpExampleRpc("validateaddress", "\"" + EXAMPLE_ADDRESS[0] + "\"") }, }.Check(request); @@ -80,10 +83,12 @@ static UniValue createmultisig(const JSONRPCRequest& request) {"address_type", RPCArg::Type::STR, /* default */ "legacy", "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."}, }, RPCResult{ - "{\n" - " \"address\":\"multisigaddress\", (string) The value of the new multisig address.\n" - " \"redeemScript\":\"script\" (string) The string value of the hex-encoded redemption script.\n" - "}\n" + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR, "address", "The value of the new multisig address."}, + {RPCResult::Type::STR_HEX, "redeemScript", "The string value of the hex-encoded redemption script."}, + {RPCResult::Type::STR, "descriptor", "The descriptor for this multisig"}, + } }, RPCExamples{ "\nCreate a multisig address from 2 public keys\n" @@ -119,9 +124,13 @@ static UniValue createmultisig(const JSONRPCRequest& request) CScript inner; const CTxDestination dest = AddAndGetMultisigDestination(required, pubkeys, output_type, keystore, inner); + // Make the descriptor + std::unique_ptr<Descriptor> descriptor = InferDescriptor(GetScriptForDestination(dest), keystore); + UniValue result(UniValue::VOBJ); result.pushKV("address", EncodeDestination(dest)); result.pushKV("redeemScript", HexStr(inner.begin(), inner.end())); + result.pushKV("descriptor", descriptor->ToString()); return result; } @@ -134,13 +143,14 @@ UniValue getdescriptorinfo(const JSONRPCRequest& request) {"descriptor", RPCArg::Type::STR, RPCArg::Optional::NO, "The descriptor."}, }, RPCResult{ - "{\n" - " \"descriptor\" : \"desc\", (string) The descriptor in canonical form, without private keys\n" - " \"checksum\" : \"chksum\", (string) The checksum for the input descriptor\n" - " \"isrange\" : true|false, (boolean) Whether the descriptor is ranged\n" - " \"issolvable\" : true|false, (boolean) Whether the descriptor is solvable\n" - " \"hasprivatekeys\" : true|false, (boolean) Whether the input descriptor contained at least one private key\n" - "}\n" + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR, "descriptor", "The descriptor in canonical form, without private keys"}, + {RPCResult::Type::STR, "checksum", "The checksum for the input descriptor"}, + {RPCResult::Type::BOOL, "isrange", "Whether the descriptor is ranged"}, + {RPCResult::Type::BOOL, "issolvable", "Whether the descriptor is solvable"}, + {RPCResult::Type::BOOL, "hasprivatekeys", "Whether the input descriptor contained at least one private key"}, + } }, RPCExamples{ "Analyse a descriptor\n" + @@ -182,7 +192,10 @@ UniValue deriveaddresses(const JSONRPCRequest& request) {"range", RPCArg::Type::RANGE, RPCArg::Optional::OMITTED_NAMED_ARG, "If a ranged descriptor is used, this specifies the end or the range (in [begin,end] notation) to derive."}, }, RPCResult{ - "[ address ] (array) the derived addresses\n" + RPCResult::Type::ARR, "", "", + { + {RPCResult::Type::STR, "address", "the derived addresses"}, + } }, RPCExamples{ "First three native segwit receive addresses\n" + @@ -251,7 +264,7 @@ static UniValue verifymessage(const JSONRPCRequest& request) {"message", RPCArg::Type::STR, RPCArg::Optional::NO, "The message that was signed."}, }, RPCResult{ - "true|false (boolean) If the signature is verified or not.\n" + RPCResult::Type::BOOL, "", "If the signature is verified or not." }, RPCExamples{ "\nUnlock the wallet for 30 seconds\n" @@ -271,31 +284,21 @@ static UniValue verifymessage(const JSONRPCRequest& request) std::string strSign = request.params[1].get_str(); std::string strMessage = request.params[2].get_str(); - CTxDestination destination = DecodeDestination(strAddress); - if (!IsValidDestination(destination)) { + switch (MessageVerify(strAddress, strSign, strMessage)) { + case MessageVerificationResult::ERR_INVALID_ADDRESS: throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address"); - } - - const PKHash *pkhash = boost::get<PKHash>(&destination); - if (!pkhash) { + case MessageVerificationResult::ERR_ADDRESS_NO_KEY: throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); - } - - bool fInvalid = false; - std::vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid); - - if (fInvalid) + case MessageVerificationResult::ERR_MALFORMED_SIGNATURE: throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Malformed base64 encoding"); - - CHashWriter ss(SER_GETHASH, 0); - ss << strMessageMagic; - ss << strMessage; - - CPubKey pubkey; - if (!pubkey.RecoverCompact(ss.GetHash(), vchSig)) + case MessageVerificationResult::ERR_PUBKEY_NOT_RECOVERED: + case MessageVerificationResult::ERR_NOT_SIGNED: return false; + case MessageVerificationResult::OK: + return true; + } - return (pubkey.GetID() == *pkhash); + return false; } static UniValue signmessagewithprivkey(const JSONRPCRequest& request) @@ -307,7 +310,7 @@ static UniValue signmessagewithprivkey(const JSONRPCRequest& request) {"message", RPCArg::Type::STR, RPCArg::Optional::NO, "The message to create a signature of."}, }, RPCResult{ - "\"signature\" (string) The signature of the message encoded in base 64\n" + RPCResult::Type::STR, "signature", "The signature of the message encoded in base 64" }, RPCExamples{ "\nCreate the signature\n" @@ -327,15 +330,13 @@ static UniValue signmessagewithprivkey(const JSONRPCRequest& request) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key"); } - CHashWriter ss(SER_GETHASH, 0); - ss << strMessageMagic; - ss << strMessage; + std::string signature; - std::vector<unsigned char> vchSig; - if (!key.SignCompact(ss.GetHash(), vchSig)) + if (!MessageSign(key, strMessage, signature)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed"); + } - return EncodeBase64(vchSig.data(), vchSig.size()); + return signature; } static UniValue setmocktime(const JSONRPCRequest& request) @@ -350,8 +351,9 @@ static UniValue setmocktime(const JSONRPCRequest& request) RPCExamples{""}, }.Check(request); - if (!Params().MineBlocksOnDemand()) - throw std::runtime_error("setmocktime for regression testing (-regtest mode) only"); + if (!Params().IsMockableChain()) { + throw std::runtime_error("setmocktime is for regression testing (-regtest mode) only"); + } // For now, don't change mocktime if we're in the middle of validation, as // this could have an effect on mempool time-based eviction, as well as @@ -366,6 +368,36 @@ static UniValue setmocktime(const JSONRPCRequest& request) return NullUniValue; } +static UniValue mockscheduler(const JSONRPCRequest& request) +{ + RPCHelpMan{"mockscheduler", + "\nBump the scheduler into the future (-regtest only)\n", + { + {"delta_time", RPCArg::Type::NUM, RPCArg::Optional::NO, "Number of seconds to forward the scheduler into the future." }, + }, + RPCResults{}, + RPCExamples{""}, + }.Check(request); + + if (!Params().IsMockableChain()) { + throw std::runtime_error("mockscheduler is for regression testing (-regtest mode) only"); + } + + // check params are valid values + RPCTypeCheck(request.params, {UniValue::VNUM}); + int64_t delta_seconds = request.params[0].get_int64(); + if ((delta_seconds <= 0) || (delta_seconds > 3600)) { + throw std::runtime_error("delta_time must be between 1 and 3600 seconds (1 hr)"); + } + + // protect against null pointer dereference + CHECK_NONFATAL(g_rpc_node); + CHECK_NONFATAL(g_rpc_node->scheduler); + g_rpc_node->scheduler->MockForward(std::chrono::seconds(delta_seconds)); + + return NullUniValue; +} + static UniValue RPCLockedMemoryInfo() { LockedPool::Stats stats = LockedPoolManager::Instance().stats(); @@ -412,19 +444,21 @@ static UniValue getmemoryinfo(const JSONRPCRequest& request) }, { RPCResult{"mode \"stats\"", - "{\n" - " \"locked\": { (json object) Information about locked memory manager\n" - " \"used\": xxxxx, (numeric) Number of bytes used\n" - " \"free\": xxxxx, (numeric) Number of bytes available in current arenas\n" - " \"total\": xxxxxxx, (numeric) Total number of bytes managed\n" - " \"locked\": xxxxxx, (numeric) Amount of bytes that succeeded locking. If this number is smaller than total, locking pages failed at some point and key data could be swapped to disk.\n" - " \"chunks_used\": xxxxx, (numeric) Number allocated chunks\n" - " \"chunks_free\": xxxxx, (numeric) Number unused chunks\n" - " }\n" - "}\n" + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::OBJ, "locked", "Information about locked memory manager", + { + {RPCResult::Type::NUM, "used", "Number of bytes used"}, + {RPCResult::Type::NUM, "free", "Number of bytes available in current arenas"}, + {RPCResult::Type::NUM, "total", "Total number of bytes managed"}, + {RPCResult::Type::NUM, "locked", "Amount of bytes that succeeded locking. If this number is smaller than total, locking pages failed at some point and key data could be swapped to disk."}, + {RPCResult::Type::NUM, "chunks_used", "Number allocated chunks"}, + {RPCResult::Type::NUM, "chunks_free", "Number unused chunks"}, + }}, + } }, RPCResult{"mode \"mallocinfo\"", - "\"<malloc version=\"1\">...\"\n" + RPCResult::Type::STR, "", "\"<malloc version=\"1\">...\"" }, }, RPCExamples{ @@ -491,10 +525,10 @@ UniValue logging(const JSONRPCRequest& request) }}, }, RPCResult{ - "{ (json object where keys are the logging categories, and values indicates its status\n" - " \"category\": true|false, (bool) if being debug logged or not. false:inactive, true:active\n" - " ...\n" - "}\n" + RPCResult::Type::OBJ_DYN, "", "keys are the logging categories, and values indicates its status", + { + {RPCResult::Type::BOOL, "category", "if being debug logged or not. false:inactive, true:active"}, + } }, RPCExamples{ HelpExampleCli("logging", "\"[\\\"all\\\"]\" \"[\\\"http\\\"]\"") @@ -570,6 +604,7 @@ static const CRPCCommand commands[] = /* Not shown in help */ { "hidden", "setmocktime", &setmocktime, {"timestamp"}}, + { "hidden", "mockscheduler", &mockscheduler, {"delta_time"}}, { "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"}}, }; diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 5e53fa5f5d..eeb617f849 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -33,7 +33,7 @@ static UniValue getconnectioncount(const JSONRPCRequest& request) "\nReturns the number of connections to other nodes.\n", {}, RPCResult{ - "n (numeric) The connection count\n" + RPCResult::Type::NUM, "", "The connection count" }, RPCExamples{ HelpExampleCli("getconnectioncount", "") @@ -77,56 +77,60 @@ static UniValue getpeerinfo(const JSONRPCRequest& request) "\nReturns data about each connected network node as a json array of objects.\n", {}, RPCResult{ - "[\n" - " {\n" - " \"id\": n, (numeric) Peer index\n" - " \"addr\":\"host:port\", (string) The IP address and port of the peer\n" - " \"addrbind\":\"ip:port\", (string) Bind address of the connection to the peer\n" - " \"addrlocal\":\"ip:port\", (string) Local address as reported by the peer\n" - " \"services\":\"xxxxxxxxxxxxxxxx\", (string) The services offered\n" - " \"servicesnames\":[ (array) the services offered, in human-readable form\n" - " \"SERVICE_NAME\", (string) the service name if it is recognised\n" - " ...\n" - " ],\n" - " \"relaytxes\":true|false, (boolean) Whether peer has asked us to relay transactions to it\n" - " \"lastsend\": ttt, (numeric) The " + UNIX_EPOCH_TIME + " of the last send\n" - " \"lastrecv\": ttt, (numeric) The " + UNIX_EPOCH_TIME + " of the last receive\n" - " \"bytessent\": n, (numeric) The total bytes sent\n" - " \"bytesrecv\": n, (numeric) The total bytes received\n" - " \"conntime\": ttt, (numeric) The " + UNIX_EPOCH_TIME + " of the connection\n" - " \"timeoffset\": ttt, (numeric) The time offset in seconds\n" - " \"pingtime\": n, (numeric) ping time (if available)\n" - " \"minping\": n, (numeric) minimum observed ping time (if any at all)\n" - " \"pingwait\": n, (numeric) ping wait (if non-zero)\n" - " \"version\": v, (numeric) The peer version, such as 70001\n" - " \"subver\": \"/Satoshi:0.8.5/\", (string) The string version\n" - " \"inbound\": true|false, (boolean) Inbound (true) or Outbound (false)\n" - " \"addnode\": true|false, (boolean) Whether connection was due to addnode/-connect or if it was an automatic/inbound connection\n" - " \"startingheight\": n, (numeric) The starting height (block) of the peer\n" - " \"banscore\": n, (numeric) The ban score\n" - " \"synced_headers\": n, (numeric) The last header we have in common with this peer\n" - " \"synced_blocks\": n, (numeric) The last block we have in common with this peer\n" - " \"inflight\": [\n" - " n, (numeric) The heights of blocks we're currently asking from this peer\n" - " ...\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" - " \"msg\": n, (numeric) The total bytes sent aggregated by message type\n" - " When a message type is not listed in this json object, the bytes sent are 0.\n" - " Only known message types can appear as keys in the object.\n" - " ...\n" - " },\n" - " \"bytesrecv_per_msg\": {\n" - " \"msg\": n, (numeric) The total bytes received aggregated by message type\n" - " When a message type is not listed in this json object, the bytes received are 0.\n" - " Only known message types can appear as keys in the object and all bytes received of unknown message types are listed under '"+NET_MESSAGE_COMMAND_OTHER+"'.\n" - " ...\n" - " }\n" - " }\n" - " ,...\n" - "]\n" + RPCResult::Type::ARR, "", "", + { + {RPCResult::Type::OBJ, "", "", + { + { + {RPCResult::Type::NUM, "id", "Peer index"}, + {RPCResult::Type::STR, "addr", "(host:port) The IP address and port of the peer"}, + {RPCResult::Type::STR, "addrbind", "(ip:port) Bind address of the connection to the peer"}, + {RPCResult::Type::STR, "addrlocal", "(ip:port) Local address as reported by the peer"}, + {RPCResult::Type::NUM, "mapped_as", "The AS in the BGP route to the peer used for diversifying\n" + "peer selection (only available if the asmap config flag is set)"}, + {RPCResult::Type::STR_HEX, "services", "The services offered"}, + {RPCResult::Type::ARR, "servicesnames", "the services offered, in human-readable form", + { + {RPCResult::Type::STR, "SERVICE_NAME", "the service name if it is recognised"} + }}, + {RPCResult::Type::BOOL, "relaytxes", "Whether peer has asked us to relay transactions to it"}, + {RPCResult::Type::NUM_TIME, "lastsend", "The " + UNIX_EPOCH_TIME + " of the last send"}, + {RPCResult::Type::NUM_TIME, "lastrecv", "The " + UNIX_EPOCH_TIME + " of the last receive"}, + {RPCResult::Type::NUM, "bytessent", "The total bytes sent"}, + {RPCResult::Type::NUM, "bytesrecv", "The total bytes received"}, + {RPCResult::Type::NUM_TIME, "conntime", "The " + UNIX_EPOCH_TIME + " of the connection"}, + {RPCResult::Type::NUM, "timeoffset", "The time offset in seconds"}, + {RPCResult::Type::NUM, "pingtime", "ping time (if available)"}, + {RPCResult::Type::NUM, "minping", "minimum observed ping time (if any at all)"}, + {RPCResult::Type::NUM, "pingwait", "ping wait (if non-zero)"}, + {RPCResult::Type::NUM, "version", "The peer version, such as 70001"}, + {RPCResult::Type::STR, "subver", "The string version"}, + {RPCResult::Type::BOOL, "inbound", "Inbound (true) or Outbound (false)"}, + {RPCResult::Type::BOOL, "addnode", "Whether connection was due to addnode/-connect or if it was an automatic/inbound connection"}, + {RPCResult::Type::NUM, "startingheight", "The starting height (block) of the peer"}, + {RPCResult::Type::NUM, "banscore", "The ban score"}, + {RPCResult::Type::NUM, "synced_headers", "The last header we have in common with this peer"}, + {RPCResult::Type::NUM, "synced_blocks", "The last block we have in common with this peer"}, + {RPCResult::Type::ARR, "inflight", "", + { + {RPCResult::Type::NUM, "n", "The heights of blocks we're currently asking from this peer"}, + }}, + {RPCResult::Type::BOOL, "whitelisted", "Whether the peer is whitelisted"}, + {RPCResult::Type::NUM, "minfeefilter", "The minimum fee rate for transactions this peer accepts"}, + {RPCResult::Type::OBJ_DYN, "bytessent_per_msg", "", + { + {RPCResult::Type::NUM, "msg", "The total bytes sent aggregated by message type\n" + "When a message type is not listed in this json object, the bytes sent are 0.\n" + "Only known message types can appear as keys in the object."} + }}, + {RPCResult::Type::OBJ, "bytesrecv_per_msg", "", + { + {RPCResult::Type::NUM, "msg", "The total bytes received aggregated by message type\n" + "When a message type is not listed in this json object, the bytes received are 0.\n" + "Only known message types can appear as keys in the object and all bytes received of unknown message types are listed under '"+NET_MESSAGE_COMMAND_OTHER+"'."} + }}, + }}, + }}, }, RPCExamples{ HelpExampleCli("getpeerinfo", "") @@ -152,6 +156,9 @@ static UniValue getpeerinfo(const JSONRPCRequest& request) obj.pushKV("addrlocal", stats.addrLocal); if (stats.addrBind.IsValid()) obj.pushKV("addrbind", stats.addrBind.ToString()); + if (stats.m_mapped_as != 0) { + obj.pushKV("mapped_as", uint64_t(stats.m_mapped_as)); + } obj.pushKV("services", strprintf("%016x", stats.nServices)); obj.pushKV("servicesnames", GetServicesNames(stats.nServices)); obj.pushKV("relaytxes", stats.fRelayTxes); @@ -161,12 +168,15 @@ static UniValue getpeerinfo(const JSONRPCRequest& request) obj.pushKV("bytesrecv", stats.nRecvBytes); obj.pushKV("conntime", stats.nTimeConnected); obj.pushKV("timeoffset", stats.nTimeOffset); - if (stats.dPingTime > 0.0) - obj.pushKV("pingtime", stats.dPingTime); - if (stats.dMinPing < static_cast<double>(std::numeric_limits<int64_t>::max())/1e6) - obj.pushKV("minping", stats.dMinPing); - if (stats.dPingWait > 0.0) - obj.pushKV("pingwait", stats.dPingWait); + if (stats.m_ping_usec > 0) { + obj.pushKV("pingtime", ((double)stats.m_ping_usec) / 1e6); + } + if (stats.m_min_ping_usec < std::numeric_limits<int64_t>::max()) { + obj.pushKV("minping", ((double)stats.m_min_ping_usec) / 1e6); + } + if (stats.m_ping_wait_usec > 0) { + obj.pushKV("pingwait", ((double)stats.m_ping_wait_usec) / 1e6); + } obj.pushKV("version", stats.nVersion); // Use the sanitized form of subver here, to avoid tricksy remote peers from // corrupting or modifying the JSON output by putting special characters in @@ -316,19 +326,22 @@ static UniValue getaddednodeinfo(const JSONRPCRequest& request) {"node", RPCArg::Type::STR, /* default */ "all nodes", "If provided, return information about this specific node, otherwise all nodes are returned."}, }, RPCResult{ - "[\n" - " {\n" - " \"addednode\" : \"192.168.0.201\", (string) The node IP address or name (as provided to addnode)\n" - " \"connected\" : true|false, (boolean) If connected\n" - " \"addresses\" : [ (list of objects) Only when connected = true\n" - " {\n" - " \"address\" : \"192.168.0.201:8333\", (string) The bitcoin server IP and port we're connected to\n" - " \"connected\" : \"outbound\" (string) connection, inbound or outbound\n" - " }\n" - " ]\n" - " }\n" - " ,...\n" - "]\n" + RPCResult::Type::ARR, "", "", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR, "addednode", "The node IP address or name (as provided to addnode)"}, + {RPCResult::Type::BOOL, "connected", "If connected"}, + {RPCResult::Type::ARR, "addresses", "Only when connected = true", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR, "address", "The bitcoin server IP and port we're connected to"}, + {RPCResult::Type::STR, "connected", "connection, inbound or outbound"}, + }}, + }}, + }}, + } }, RPCExamples{ HelpExampleCli("getaddednodeinfo", "\"192.168.0.201\"") @@ -382,20 +395,21 @@ static UniValue getnettotals(const JSONRPCRequest& request) "and current time.\n", {}, RPCResult{ - "{\n" - " \"totalbytesrecv\": n, (numeric) Total bytes received\n" - " \"totalbytessent\": n, (numeric) Total bytes sent\n" - " \"timemillis\": t, (numeric) Current UNIX time in milliseconds\n" - " \"uploadtarget\":\n" - " {\n" - " \"timeframe\": n, (numeric) Length of the measuring timeframe in seconds\n" - " \"target\": n, (numeric) Target in bytes\n" - " \"target_reached\": true|false, (boolean) True if target is reached\n" - " \"serve_historical_blocks\": true|false, (boolean) True if serving historical blocks\n" - " \"bytes_left_in_cycle\": t, (numeric) Bytes left in current time cycle\n" - " \"time_left_in_cycle\": t (numeric) Seconds left in current time cycle\n" - " }\n" - "}\n" + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::NUM, "totalbytesrecv", "Total bytes received"}, + {RPCResult::Type::NUM, "totalbytessent", "Total bytes sent"}, + {RPCResult::Type::NUM_TIME, "timemillis", "Current UNIX time in milliseconds"}, + {RPCResult::Type::OBJ, "uploadtarget", "", + { + {RPCResult::Type::NUM, "timeframe", "Length of the measuring timeframe in seconds"}, + {RPCResult::Type::NUM, "target", "Target in bytes"}, + {RPCResult::Type::BOOL, "target_reached", "True if target is reached"}, + {RPCResult::Type::BOOL, "serve_historical_blocks", "True if serving historical blocks"}, + {RPCResult::Type::NUM, "bytes_left_in_cycle", "Bytes left in current time cycle"}, + {RPCResult::Type::NUM, "time_left_in_cycle", "Seconds left in current time cycle"}, + }}, + } }, RPCExamples{ HelpExampleCli("getnettotals", "") @@ -448,41 +462,44 @@ static UniValue getnetworkinfo(const JSONRPCRequest& request) "Returns an object containing various state info regarding P2P networking.\n", {}, RPCResult{ - "{\n" - " \"version\": xxxxx, (numeric) the server version\n" - " \"subversion\": \"/Satoshi:x.x.x/\", (string) the server subversion string\n" - " \"protocolversion\": xxxxx, (numeric) the protocol version\n" - " \"localservices\": \"xxxxxxxxxxxxxxxx\", (string) the services we offer to the network\n" - " \"localservicesnames\": [ (array) the services we offer to the network, in human-readable form\n" - " \"SERVICE_NAME\", (string) the service name\n" - " ...\n" - " ],\n" - " \"localrelay\": true|false, (bool) true if transaction relay is requested from peers\n" - " \"timeoffset\": xxxxx, (numeric) the time offset\n" - " \"connections\": xxxxx, (numeric) the number of connections\n" - " \"networkactive\": true|false, (bool) whether p2p networking is enabled\n" - " \"networks\": [ (array) information per network\n" - " {\n" - " \"name\": \"xxx\", (string) network (ipv4, ipv6 or onion)\n" - " \"limited\": true|false, (boolean) is the network limited using -onlynet?\n" - " \"reachable\": true|false, (boolean) is the network reachable?\n" - " \"proxy\": \"host:port\" (string) the proxy that is used for this network, or empty if none\n" - " \"proxy_randomize_credentials\": true|false, (string) Whether randomized credentials are used\n" - " }\n" - " ,...\n" - " ],\n" - " \"relayfee\": x.xxxxxxxx, (numeric) minimum relay fee for transactions in " + CURRENCY_UNIT + "/kB\n" - " \"incrementalfee\": x.xxxxxxxx, (numeric) minimum fee increment for mempool limiting or BIP 125 replacement in " + CURRENCY_UNIT + "/kB\n" - " \"localaddresses\": [ (array) list of local addresses\n" - " {\n" - " \"address\": \"xxxx\", (string) network address\n" - " \"port\": xxx, (numeric) network port\n" - " \"score\": xxx (numeric) relative score\n" - " }\n" - " ,...\n" - " ]\n" - " \"warnings\": \"...\" (string) any network and blockchain warnings\n" - "}\n" + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::NUM, "version", "the server version"}, + {RPCResult::Type::STR, "subversion", "the server subversion string"}, + {RPCResult::Type::NUM, "protocolversion", "the protocol version"}, + {RPCResult::Type::STR_HEX, "localservices", "the services we offer to the network"}, + {RPCResult::Type::ARR, "localservicesnames", "the services we offer to the network, in human-readable form", + { + {RPCResult::Type::STR, "SERVICE_NAME", "the service name"}, + }}, + {RPCResult::Type::BOOL, "localrelay", "true if transaction relay is requested from peers"}, + {RPCResult::Type::NUM, "timeoffset", "the time offset"}, + {RPCResult::Type::NUM, "connections", "the number of connections"}, + {RPCResult::Type::BOOL, "networkactive", "whether p2p networking is enabled"}, + {RPCResult::Type::ARR, "networks", "information per network", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR, "name", "network (ipv4, ipv6 or onion)"}, + {RPCResult::Type::BOOL, "limited", "is the network limited using -onlynet?"}, + {RPCResult::Type::BOOL, "reachable", "is the network reachable?"}, + {RPCResult::Type::STR, "proxy", "(\"host:port\") the proxy that is used for this network, or empty if none"}, + {RPCResult::Type::BOOL, "proxy_randomize_credentials", "Whether randomized credentials are used"}, + }}, + }}, + {RPCResult::Type::NUM, "relayfee", "minimum relay fee for transactions in " + CURRENCY_UNIT + "/kB"}, + {RPCResult::Type::NUM, "incrementalfee", "minimum fee increment for mempool limiting or BIP 125 replacement in " + CURRENCY_UNIT + "/kB"}, + {RPCResult::Type::ARR, "localaddresses", "list of local addresses", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR, "address", "network address"}, + {RPCResult::Type::NUM, "port", "network port"}, + {RPCResult::Type::NUM, "score", "relative score"}, + }}, + }}, + {RPCResult::Type::STR, "warnings", "any network and blockchain warnings"}, + } }, RPCExamples{ HelpExampleCli("getnetworkinfo", "") @@ -562,11 +579,11 @@ static UniValue setban(const JSONRPCRequest& request) if (!isSubnet) { CNetAddr resolved; - LookupHost(request.params[0].get_str().c_str(), resolved, false); + LookupHost(request.params[0].get_str(), resolved, false); netAddr = resolved; } else - LookupSubNet(request.params[0].get_str().c_str(), subNet); + LookupSubNet(request.params[0].get_str(), subNet); if (! (isSubnet ? subNet.IsValid() : netAddr.IsValid()) ) throw JSONRPCError(RPC_CLIENT_INVALID_IP_OR_SUBNET, "Error: Invalid IP/Subnet"); @@ -689,15 +706,16 @@ static UniValue getnodeaddresses(const JSONRPCRequest& request) {"count", RPCArg::Type::NUM, /* default */ "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."}, }, RPCResult{ - "[\n" - " {\n" - " \"time\": ttt, (numeric) The " + UNIX_EPOCH_TIME + " 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" + RPCResult::Type::ARR, "", "", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::NUM_TIME, "time", "The " + UNIX_EPOCH_TIME + " of when the node was last seen"}, + {RPCResult::Type::NUM, "services", "The services offered"}, + {RPCResult::Type::STR, "address", "The address of the node"}, + {RPCResult::Type::NUM, "port", "The port of the node"}, + }}, + } }, RPCExamples{ HelpExampleCli("getnodeaddresses", "8") diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 02aaf929e9..37e515ddfe 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -94,54 +94,62 @@ static UniValue getrawtransaction(const JSONRPCRequest& request) }, { RPCResult{"if verbose is not set or set to false", - "\"data\" (string) The serialized, hex-encoded data for 'txid'\n" + RPCResult::Type::STR, "data", "The serialized, hex-encoded data for 'txid'" }, RPCResult{"if verbose is set to true", - "{\n" - " \"in_active_chain\": b, (bool) Whether specified block is in the active chain or not (only present with explicit \"blockhash\" argument)\n" - " \"hex\" : \"data\", (string) The serialized, hex-encoded data for 'txid'\n" - " \"txid\" : \"id\", (string) The transaction id (same as provided)\n" - " \"hash\" : \"id\", (string) The transaction hash (differs from txid for witness transactions)\n" - " \"size\" : n, (numeric) The serialized transaction size\n" - " \"vsize\" : n, (numeric) The virtual transaction size (differs from size for witness transactions)\n" - " \"weight\" : n, (numeric) The transaction's weight (between vsize*4-3 and vsize*4)\n" - " \"version\" : n, (numeric) The version\n" - " \"locktime\" : ttt, (numeric) The lock time\n" - " \"vin\" : [ (array of json objects)\n" - " {\n" - " \"txid\": \"id\", (string) The transaction id\n" - " \"vout\": n, (numeric) \n" - " \"scriptSig\": { (json object) The script\n" - " \"asm\": \"asm\", (string) asm\n" - " \"hex\": \"hex\" (string) hex\n" - " },\n" - " \"sequence\": n (numeric) The script sequence number\n" - " \"txinwitness\": [\"hex\", ...] (array of string) hex-encoded witness data (if any)\n" - " }\n" - " ,...\n" - " ],\n" - " \"vout\" : [ (array of json objects)\n" - " {\n" - " \"value\" : x.xxx, (numeric) The value in " + CURRENCY_UNIT + "\n" - " \"n\" : n, (numeric) index\n" - " \"scriptPubKey\" : { (json object)\n" - " \"asm\" : \"asm\", (string) the asm\n" - " \"hex\" : \"hex\", (string) the hex\n" - " \"reqSigs\" : n, (numeric) The required sigs\n" - " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n" - " \"addresses\" : [ (json array of string)\n" - " \"address\" (string) bitcoin address\n" - " ,...\n" - " ]\n" - " }\n" - " }\n" - " ,...\n" - " ],\n" - " \"blockhash\" : \"hash\", (string) the block hash\n" - " \"confirmations\" : n, (numeric) The confirmations\n" - " \"blocktime\" : ttt (numeric) The block time expressed in " + UNIX_EPOCH_TIME + "\n" - " \"time\" : ttt, (numeric) Same as \"blocktime\"\n" - "}\n" + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::BOOL, "in_active_chain", "Whether specified block is in the active chain or not (only present with explicit \"blockhash\" argument)"}, + {RPCResult::Type::STR_HEX, "hex", "The serialized, hex-encoded data for 'txid'"}, + {RPCResult::Type::STR_HEX, "txid", "The transaction id (same as provided)"}, + {RPCResult::Type::STR_HEX, "hash", "The transaction hash (differs from txid for witness transactions)"}, + {RPCResult::Type::NUM, "size", "The serialized transaction size"}, + {RPCResult::Type::NUM, "vsize", "The virtual transaction size (differs from size for witness transactions)"}, + {RPCResult::Type::NUM, "weight", "The transaction's weight (between vsize*4-3 and vsize*4)"}, + {RPCResult::Type::NUM, "version", "The version"}, + {RPCResult::Type::NUM_TIME, "locktime", "The lock time"}, + {RPCResult::Type::ARR, "vin", "", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "txid", "The transaction id"}, + {RPCResult::Type::STR, "vout", ""}, + {RPCResult::Type::OBJ, "scriptSig", "The script", + { + {RPCResult::Type::STR, "asm", "asm"}, + {RPCResult::Type::STR_HEX, "hex", "hex"}, + }}, + {RPCResult::Type::NUM, "sequence", "The script sequence number"}, + {RPCResult::Type::ARR, "txinwitness", "", + { + {RPCResult::Type::STR_HEX, "hex", "hex-encoded witness data (if any)"}, + }}, + }}, + }}, + {RPCResult::Type::ARR, "vout", "", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::NUM, "value", "The value in " + CURRENCY_UNIT}, + {RPCResult::Type::NUM, "n", "index"}, + {RPCResult::Type::OBJ, "scriptPubKey", "", + { + {RPCResult::Type::STR, "asm", "the asm"}, + {RPCResult::Type::STR, "hex", "the hex"}, + {RPCResult::Type::NUM, "reqSigs", "The required sigs"}, + {RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"}, + {RPCResult::Type::ARR, "addresses", "", + { + {RPCResult::Type::STR, "address", "bitcoin address"}, + }}, + }}, + }}, + }}, + {RPCResult::Type::STR_HEX, "blockhash", "the block hash"}, + {RPCResult::Type::NUM, "confirmations", "The confirmations"}, + {RPCResult::Type::NUM_TIME, "blocktime", "The block time expressed in " + UNIX_EPOCH_TIME}, + {RPCResult::Type::NUM, "time", "Same as \"blocktime\""}, + } }, }, RPCExamples{ @@ -230,7 +238,7 @@ static UniValue gettxoutproof(const JSONRPCRequest& request) {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED_NAMED_ARG, "If specified, looks for txid in the block with this hash"}, }, RPCResult{ - "\"data\" (string) A string that is a serialized, hex-encoded data for the proof.\n" + RPCResult::Type::STR, "data", "A string that is a serialized, hex-encoded data for the proof." }, RPCExamples{""}, }.Check(request); @@ -315,7 +323,10 @@ static UniValue verifytxoutproof(const JSONRPCRequest& request) {"proof", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex-encoded proof generated by gettxoutproof"}, }, RPCResult{ - "[\"txid\"] (array, strings) The txid(s) which the proof commits to, or empty array if the proof can not be validated.\n" + RPCResult::Type::ARR, "", "", + { + {RPCResult::Type::STR_HEX, "txid", "The txid(s) which the proof commits to, or empty array if the proof can not be validated."}, + } }, RPCExamples{""}, }.Check(request); @@ -390,7 +401,7 @@ static UniValue createrawtransaction(const JSONRPCRequest& request) " Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible."}, }, RPCResult{ - "\"transaction\" (string) hex string of the transaction\n" + RPCResult::Type::STR_HEX, "transaction", "hex string of the transaction" }, RPCExamples{ HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"address\\\":0.01}]\"") @@ -432,45 +443,53 @@ static UniValue decoderawtransaction(const JSONRPCRequest& request) }, }, RPCResult{ - "{\n" - " \"txid\" : \"id\", (string) The transaction id\n" - " \"hash\" : \"id\", (string) The transaction hash (differs from txid for witness transactions)\n" - " \"size\" : n, (numeric) The transaction size\n" - " \"vsize\" : n, (numeric) The virtual transaction size (differs from size for witness transactions)\n" - " \"weight\" : n, (numeric) The transaction's weight (between vsize*4 - 3 and vsize*4)\n" - " \"version\" : n, (numeric) The version\n" - " \"locktime\" : ttt, (numeric) The lock time\n" - " \"vin\" : [ (array of json objects)\n" - " {\n" - " \"txid\": \"id\", (string) The transaction id\n" - " \"vout\": n, (numeric) The output number\n" - " \"scriptSig\": { (json object) The script\n" - " \"asm\": \"asm\", (string) asm\n" - " \"hex\": \"hex\" (string) hex\n" - " },\n" - " \"txinwitness\": [\"hex\", ...] (array of string) hex-encoded witness data (if any)\n" - " \"sequence\": n (numeric) The script sequence number\n" - " }\n" - " ,...\n" - " ],\n" - " \"vout\" : [ (array of json objects)\n" - " {\n" - " \"value\" : x.xxx, (numeric) The value in " + CURRENCY_UNIT + "\n" - " \"n\" : n, (numeric) index\n" - " \"scriptPubKey\" : { (json object)\n" - " \"asm\" : \"asm\", (string) the asm\n" - " \"hex\" : \"hex\", (string) the hex\n" - " \"reqSigs\" : n, (numeric) The required sigs\n" - " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n" - " \"addresses\" : [ (json array of string)\n" - " \"12tvKAXCxZjSmdNbao16dKXC8tRWfcF5oc\" (string) bitcoin address\n" - " ,...\n" - " ]\n" - " }\n" - " }\n" - " ,...\n" - " ],\n" - "}\n" + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "txid", "The transaction id"}, + {RPCResult::Type::STR_HEX, "hash", "The transaction hash (differs from txid for witness transactions)"}, + {RPCResult::Type::NUM, "size", "The transaction size"}, + {RPCResult::Type::NUM, "vsize", "The virtual transaction size (differs from size for witness transactions)"}, + {RPCResult::Type::NUM, "weight", "The transaction's weight (between vsize*4 - 3 and vsize*4)"}, + {RPCResult::Type::NUM, "version", "The version"}, + {RPCResult::Type::NUM_TIME, "locktime", "The lock time"}, + {RPCResult::Type::ARR, "vin", "", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "txid", "The transaction id"}, + {RPCResult::Type::NUM, "vout", "The output number"}, + {RPCResult::Type::OBJ, "scriptSig", "The script", + { + {RPCResult::Type::STR, "asm", "asm"}, + {RPCResult::Type::STR_HEX, "hex", "hex"}, + }}, + {RPCResult::Type::ARR, "txinwitness", "", + { + {RPCResult::Type::STR_HEX, "hex", "hex-encoded witness data (if any)"}, + }}, + {RPCResult::Type::NUM, "sequence", "The script sequence number"}, + }}, + }}, + {RPCResult::Type::ARR, "vout", "", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::NUM, "value", "The value in " + CURRENCY_UNIT}, + {RPCResult::Type::NUM, "n", "index"}, + {RPCResult::Type::OBJ, "scriptPubKey", "", + { + {RPCResult::Type::STR, "asm", "the asm"}, + {RPCResult::Type::STR_HEX, "hex", "the hex"}, + {RPCResult::Type::NUM, "reqSigs", "The required sigs"}, + {RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"}, + {RPCResult::Type::ARR, "addresses", "", + { + {RPCResult::Type::STR, "address", "bitcoin address"}, + }}, + }}, + }}, + }}, + } }, RPCExamples{ HelpExampleCli("decoderawtransaction", "\"hexstring\"") @@ -513,26 +532,29 @@ static UniValue decodescript(const JSONRPCRequest& request) {"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "the hex-encoded script"}, }, RPCResult{ - "{\n" - " \"asm\":\"asm\", (string) Script public key\n" - " \"type\":\"type\", (string) The output type (e.g. "+GetAllOutputTypes()+")\n" - " \"reqSigs\": n, (numeric) The required signatures\n" - " \"addresses\": [ (json array of string)\n" - " \"address\" (string) bitcoin address\n" - " ,...\n" - " ],\n" - " \"p2sh\":\"str\" (string) address of P2SH script wrapping this redeem script (not returned if the script is already a P2SH).\n" - " \"segwit\": { (json object) Result of a witness script public key wrapping this redeem script (not returned if the script is a P2SH or witness).\n" - " \"asm\":\"str\", (string) String representation of the script public key\n" - " \"hex\":\"hexstr\", (string) Hex string of the script public key\n" - " \"type\":\"str\", (string) The type of the script public key (e.g. witness_v0_keyhash or witness_v0_scripthash)\n" - " \"reqSigs\": n, (numeric) The required signatures (always 1)\n" - " \"addresses\": [ (json array of string) (always length 1)\n" - " \"address\" (string) segwit address\n" - " ,...\n" - " ],\n" - " \"p2sh-segwit\":\"str\" (string) address of the P2SH script wrapping this witness redeem script.\n" - "}\n" + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR, "asm", "Script public key"}, + {RPCResult::Type::STR, "type", "The output type (e.g. "+GetAllOutputTypes()+")"}, + {RPCResult::Type::NUM, "reqSigs", "The required signatures"}, + {RPCResult::Type::ARR, "addresses", "", + { + {RPCResult::Type::STR, "address", "bitcoin address"}, + }}, + {RPCResult::Type::STR, "p2sh", "address of P2SH script wrapping this redeem script (not returned if the script is already a P2SH)"}, + {RPCResult::Type::OBJ, "segwit", "Result of a witness script public key wrapping this redeem script (not returned if the script is a P2SH or witness)", + { + {RPCResult::Type::STR, "asm", "String representation of the script public key"}, + {RPCResult::Type::STR_HEX, "hex", "Hex string of the script public key"}, + {RPCResult::Type::STR, "type", "The type of the script public key (e.g. witness_v0_keyhash or witness_v0_scripthash)"}, + {RPCResult::Type::NUM, "reqSigs", "The required signatures (always 1)"}, + {RPCResult::Type::ARR, "addresses", "(always length 1)", + { + {RPCResult::Type::STR, "address", "segwit address"}, + }}, + {RPCResult::Type::STR, "p2sh-segwit", "address of the P2SH script wrapping this witness redeem script"}, + }}, + } }, RPCExamples{ HelpExampleCli("decodescript", "\"hexstring\"") @@ -607,7 +629,7 @@ static UniValue combinerawtransaction(const JSONRPCRequest& request) }, }, RPCResult{ - "\"hex\" (string) The hex-encoded raw transaction with signature(s)\n" + RPCResult::Type::STR, "", "The hex-encoded raw transaction with signature(s)" }, RPCExamples{ HelpExampleCli("combinerawtransaction", R"('["myhex1", "myhex2", "myhex3"]')") @@ -715,20 +737,22 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request) }, }, RPCResult{ - "{\n" - " \"hex\" : \"value\", (string) The hex-encoded raw transaction with signature(s)\n" - " \"complete\" : true|false, (boolean) If the transaction has a complete set of signatures\n" - " \"errors\" : [ (json array of objects) Script verification errors (if there are any)\n" - " {\n" - " \"txid\" : \"hash\", (string) The hash of the referenced, previous transaction\n" - " \"vout\" : n, (numeric) The index of the output to spent and used as input\n" - " \"scriptSig\" : \"hex\", (string) The hex-encoded signature script\n" - " \"sequence\" : n, (numeric) Script sequence number\n" - " \"error\" : \"text\" (string) Verification or signing error related to the input\n" - " }\n" - " ,...\n" - " ]\n" - "}\n" + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "hex", "The hex-encoded raw transaction with signature(s)"}, + {RPCResult::Type::BOOL, "complete", "If the transaction has a complete set of signatures"}, + {RPCResult::Type::ARR, "errors", "Script verification errors (if there are any)", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "txid", "The hash of the referenced, previous transaction"}, + {RPCResult::Type::NUM, "vout", "The index of the output to spent and used as input"}, + {RPCResult::Type::STR_HEX, "scriptSig", "The hex-encoded signature script"}, + {RPCResult::Type::NUM, "sequence", "Script sequence number"}, + {RPCResult::Type::STR, "error", "Verification or signing error related to the input"}, + }}, + }}, + } }, RPCExamples{ HelpExampleCli("signrawtransactionwithkey", "\"myhex\" \"[\\\"key1\\\",\\\"key2\\\"]\"") @@ -784,7 +808,7 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request) "/kB.\nSet to 0 to accept any fee rate.\n"}, }, RPCResult{ - "\"hex\" (string) The transaction hash in hex\n" + RPCResult::Type::STR_HEX, "", "The transaction hash in hex" }, RPCExamples{ "\nCreate a transaction\n" @@ -846,14 +870,16 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request) {"maxfeerate", RPCArg::Type::AMOUNT, /* default */ FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK()), "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT + "/kB\n"}, }, RPCResult{ - "[ (array) The result of the mempool acceptance test for each raw transaction in the input array.\n" - " Length is exactly one for now.\n" - " {\n" - " \"txid\" (string) The transaction hash in hex\n" - " \"allowed\" (boolean) If the mempool allows this tx to be inserted\n" - " \"reject-reason\" (string) Rejection string (only present when 'allowed' is false)\n" - " }\n" - "]\n" + RPCResult::Type::ARR, "", "The result of the mempool acceptance test for each raw transaction in the input array.\n" + "Length is exactly one for now.", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"}, + {RPCResult::Type::BOOL, "allowed", "If the mempool allows this tx to be inserted"}, + {RPCResult::Type::STR, "reject-reason", "Rejection string (only present when 'allowed' is false)"}, + }}, + } }, RPCExamples{ "\nCreate a transaction\n" @@ -950,92 +976,108 @@ UniValue decodepsbt(const JSONRPCRequest& request) {"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "The PSBT base64 string"}, }, RPCResult{ - "{\n" - " \"tx\" : { (json object) The decoded network-serialized unsigned transaction.\n" - " ... The layout is the same as the output of decoderawtransaction.\n" - " },\n" - " \"unknown\" : { (json object) The unknown global fields\n" - " \"key\" : \"value\" (key-value pair) An unknown key-value pair\n" - " ...\n" - " },\n" - " \"inputs\" : [ (array of json objects)\n" - " {\n" - " \"non_witness_utxo\" : { (json object, optional) Decoded network transaction for non-witness UTXOs\n" - " ...\n" - " },\n" - " \"witness_utxo\" : { (json object, optional) Transaction output for witness UTXOs\n" - " \"amount\" : x.xxx, (numeric) The value in " + CURRENCY_UNIT + "\n" - " \"scriptPubKey\" : { (json object)\n" - " \"asm\" : \"asm\", (string) The asm\n" - " \"hex\" : \"hex\", (string) The hex\n" - " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n" - " \"address\" : \"address\" (string) Bitcoin address if there is one\n" - " }\n" - " },\n" - " \"partial_signatures\" : { (json object, optional)\n" - " \"pubkey\" : \"signature\", (string) The public key and signature that corresponds to it.\n" - " ,...\n" - " }\n" - " \"sighash\" : \"type\", (string, optional) The sighash type to be used\n" - " \"redeem_script\" : { (json object, optional)\n" - " \"asm\" : \"asm\", (string) The asm\n" - " \"hex\" : \"hex\", (string) The hex\n" - " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n" - " }\n" - " \"witness_script\" : { (json object, optional)\n" - " \"asm\" : \"asm\", (string) The asm\n" - " \"hex\" : \"hex\", (string) The hex\n" - " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n" - " }\n" - " \"bip32_derivs\" : { (json object, optional)\n" - " \"pubkey\" : { (json object, optional) The public key with the derivation path as the value.\n" - " \"master_fingerprint\" : \"fingerprint\" (string) The fingerprint of the master key\n" - " \"path\" : \"path\", (string) The path\n" - " }\n" - " ,...\n" - " }\n" - " \"final_scriptsig\" : { (json object, optional)\n" - " \"asm\" : \"asm\", (string) The asm\n" - " \"hex\" : \"hex\", (string) The hex\n" - " }\n" - " \"final_scriptwitness\": [\"hex\", ...] (array of string) hex-encoded witness data (if any)\n" - " \"unknown\" : { (json object) The unknown global fields\n" - " \"key\" : \"value\" (key-value pair) An unknown key-value pair\n" - " ...\n" - " },\n" - " }\n" - " ,...\n" - " ]\n" - " \"outputs\" : [ (array of json objects)\n" - " {\n" - " \"redeem_script\" : { (json object, optional)\n" - " \"asm\" : \"asm\", (string) The asm\n" - " \"hex\" : \"hex\", (string) The hex\n" - " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n" - " }\n" - " \"witness_script\" : { (json object, optional)\n" - " \"asm\" : \"asm\", (string) The asm\n" - " \"hex\" : \"hex\", (string) The hex\n" - " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n" - " }\n" - " \"bip32_derivs\" : [ (array of json objects, optional)\n" - " {\n" - " \"pubkey\" : \"pubkey\", (string) The public key this path corresponds to\n" - " \"master_fingerprint\" : \"fingerprint\" (string) The fingerprint of the master key\n" - " \"path\" : \"path\", (string) The path\n" - " }\n" - " }\n" - " ,...\n" - " ],\n" - " \"unknown\" : { (json object) The unknown global fields\n" - " \"key\" : \"value\" (key-value pair) An unknown key-value pair\n" - " ...\n" - " },\n" - " }\n" - " ,...\n" - " ]\n" - " \"fee\" : fee (numeric, optional) The transaction fee paid if all UTXOs slots in the PSBT have been filled.\n" - "}\n" + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::OBJ, "tx", "The decoded network-serialized unsigned transaction.", + { + {RPCResult::Type::ELISION, "", "The layout is the same as the output of decoderawtransaction."}, + }}, + {RPCResult::Type::OBJ_DYN, "unknown", "The unknown global fields", + { + {RPCResult::Type::STR_HEX, "key", "(key-value pair) An unknown key-value pair"}, + }}, + {RPCResult::Type::ARR, "inputs", "", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::OBJ, "non_witness_utxo", /* optional */ true, "Decoded network transaction for non-witness UTXOs", + { + {RPCResult::Type::ELISION, "",""}, + }}, + {RPCResult::Type::OBJ, "witness_utxo", /* optional */ true, "Transaction output for witness UTXOs", + { + {RPCResult::Type::NUM, "amount", "The value in " + CURRENCY_UNIT}, + {RPCResult::Type::OBJ, "scriptPubKey", "", + { + {RPCResult::Type::STR, "asm", "The asm"}, + {RPCResult::Type::STR_HEX, "hex", "The hex"}, + {RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"}, + {RPCResult::Type::STR, "address"," Bitcoin address if there is one"}, + }}, + }}, + {RPCResult::Type::OBJ_DYN, "partial_signatures", /* optional */ true, "", + { + {RPCResult::Type::STR, "pubkey", "The public key and signature that corresponds to it."}, + }}, + {RPCResult::Type::STR, "sighash", /* optional */ true, "The sighash type to be used"}, + {RPCResult::Type::OBJ, "redeem_script", /* optional */ true, "", + { + {RPCResult::Type::STR, "asm", "The asm"}, + {RPCResult::Type::STR_HEX, "hex", "The hex"}, + {RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"}, + }}, + {RPCResult::Type::OBJ, "witness_script", /* optional */ true, "", + { + {RPCResult::Type::STR, "asm", "The asm"}, + {RPCResult::Type::STR_HEX, "hex", "The hex"}, + {RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"}, + }}, + {RPCResult::Type::ARR, "bip32_derivs", /* optional */ true, "", + { + {RPCResult::Type::OBJ, "pubkey", /* optional */ true, "The public key with the derivation path as the value.", + { + {RPCResult::Type::STR, "master_fingerprint", "The fingerprint of the master key"}, + {RPCResult::Type::STR, "path", "The path"}, + }}, + }}, + {RPCResult::Type::OBJ, "final_scriptsig", /* optional */ true, "", + { + {RPCResult::Type::STR, "asm", "The asm"}, + {RPCResult::Type::STR, "hex", "The hex"}, + }}, + {RPCResult::Type::ARR, "final_scriptwitness", "", + { + {RPCResult::Type::STR_HEX, "", "hex-encoded witness data (if any)"}, + }}, + {RPCResult::Type::OBJ_DYN, "unknown", "The unknown global fields", + { + {RPCResult::Type::STR_HEX, "key", "(key-value pair) An unknown key-value pair"}, + }}, + }}, + }}, + {RPCResult::Type::ARR, "outputs", "", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::OBJ, "redeem_script", /* optional */ true, "", + { + {RPCResult::Type::STR, "asm", "The asm"}, + {RPCResult::Type::STR_HEX, "hex", "The hex"}, + {RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"}, + }}, + {RPCResult::Type::OBJ, "witness_script", /* optional */ true, "", + { + {RPCResult::Type::STR, "asm", "The asm"}, + {RPCResult::Type::STR_HEX, "hex", "The hex"}, + {RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"}, + }}, + {RPCResult::Type::ARR, "bip32_derivs", /* optional */ true, "", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR, "pubkey", "The public key this path corresponds to"}, + {RPCResult::Type::STR, "master_fingerprint", "The fingerprint of the master key"}, + {RPCResult::Type::STR, "path", "The path"}, + }}, + }}, + {RPCResult::Type::OBJ_DYN, "unknown", "The unknown global fields", + { + {RPCResult::Type::STR_HEX, "key", "(key-value pair) An unknown key-value pair"}, + }}, + }}, + }}, + {RPCResult::Type::STR_AMOUNT, "fee", /* optional */ true, "The transaction fee paid if all UTXOs slots in the PSBT have been filled."}, + } }, RPCExamples{ HelpExampleCli("decodepsbt", "\"psbt\"") @@ -1079,7 +1121,12 @@ UniValue decodepsbt(const JSONRPCRequest& request) UniValue out(UniValue::VOBJ); out.pushKV("amount", ValueFromAmount(txout.nValue)); - total_in += txout.nValue; + if (MoneyRange(txout.nValue) && MoneyRange(total_in + txout.nValue)) { + total_in += txout.nValue; + } else { + // Hack to just not show fee later + have_all_utxos = false; + } UniValue o(UniValue::VOBJ); ScriptToUniv(txout.scriptPubKey, o, true); @@ -1089,7 +1136,13 @@ UniValue decodepsbt(const JSONRPCRequest& request) UniValue non_wit(UniValue::VOBJ); TxToUniv(*input.non_witness_utxo, uint256(), non_wit, false); in.pushKV("non_witness_utxo", non_wit); - total_in += input.non_witness_utxo->vout[psbtx.tx->vin[i].prevout.n].nValue; + CAmount utxo_val = input.non_witness_utxo->vout[psbtx.tx->vin[i].prevout.n].nValue; + if (MoneyRange(utxo_val) && MoneyRange(total_in + utxo_val)) { + total_in += utxo_val; + } else { + // Hack to just not show fee later + have_all_utxos = false; + } } else { have_all_utxos = false; } @@ -1205,7 +1258,12 @@ UniValue decodepsbt(const JSONRPCRequest& request) outputs.push_back(out); // Fee calculation - output_value += psbtx.tx->vout[i].nValue; + if (MoneyRange(psbtx.tx->vout[i].nValue) && MoneyRange(output_value + psbtx.tx->vout[i].nValue)) { + output_value += psbtx.tx->vout[i].nValue; + } else { + // Hack to just not show fee later + have_all_utxos = false; + } } result.pushKV("outputs", outputs); if (have_all_utxos) { @@ -1228,7 +1286,7 @@ UniValue combinepsbt(const JSONRPCRequest& request) }, }, RPCResult{ - " \"psbt\" (string) The base64-encoded partially signed transaction\n" + RPCResult::Type::STR, "", "The base64-encoded partially signed transaction" }, RPCExamples{ HelpExampleCli("combinepsbt", R"('["mybase64_1", "mybase64_2", "mybase64_3"]')") @@ -1276,12 +1334,12 @@ UniValue finalizepsbt(const JSONRPCRequest& request) " extract and return the complete transaction in normal network serialization instead of the PSBT."}, }, RPCResult{ - "{\n" - " \"psbt\" : \"value\", (string) The base64-encoded partially signed transaction if not extracted\n" - " \"hex\" : \"value\", (string) The hex-encoded network transaction if extracted\n" - " \"complete\" : true|false, (boolean) If the transaction has a complete set of signatures\n" - " ]\n" - "}\n" + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR, "psbt", "The base64-encoded partially signed transaction if not extracted"}, + {RPCResult::Type::STR_HEX, "hex", "The hex-encoded network transaction if extracted"}, + {RPCResult::Type::BOOL, "complete", "If the transaction has a complete set of signatures"}, + } }, RPCExamples{ HelpExampleCli("finalizepsbt", "\"psbt\"") @@ -1359,7 +1417,7 @@ UniValue createpsbt(const JSONRPCRequest& request) " Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible."}, }, RPCResult{ - " \"psbt\" (string) The resulting raw transaction (base64-encoded string)\n" + RPCResult::Type::STR, "", "The resulting raw transaction (base64-encoded string)" }, RPCExamples{ HelpExampleCli("createpsbt", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"data\\\":\\\"00010203\\\"}]\"") @@ -1416,7 +1474,7 @@ UniValue converttopsbt(const JSONRPCRequest& request) }, }, RPCResult{ - " \"psbt\" (string) The resulting raw transaction (base64-encoded string)\n" + RPCResult::Type::STR, "", "The resulting raw transaction (base64-encoded string)" }, RPCExamples{ "\nCreate a transaction\n" @@ -1480,7 +1538,7 @@ UniValue utxoupdatepsbt(const JSONRPCRequest& request) }}, }, RPCResult { - " \"psbt\" (string) The base64-encoded partially signed transaction with inputs updated\n" + RPCResult::Type::STR, "", "The base64-encoded partially signed transaction with inputs updated" }, RPCExamples { HelpExampleCli("utxoupdatepsbt", "\"psbt\"") @@ -1565,7 +1623,7 @@ UniValue joinpsbts(const JSONRPCRequest& request) }} }, RPCResult { - " \"psbt\" (string) The base64-encoded partially signed transaction\n" + RPCResult::Type::STR, "", "The base64-encoded partially signed transaction" }, RPCExamples { HelpExampleCli("joinpsbts", "\"psbt\"") @@ -1654,31 +1712,36 @@ UniValue analyzepsbt(const JSONRPCRequest& request) {"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "A base64 string of a PSBT"} }, RPCResult { - "{\n" - " \"inputs\" : [ (array of json objects)\n" - " {\n" - " \"has_utxo\" : true|false (boolean) Whether a UTXO is provided\n" - " \"is_final\" : true|false (boolean) Whether the input is finalized\n" - " \"missing\" : { (json object, optional) Things that are missing that are required to complete this input\n" - " \"pubkeys\" : [ (array, optional)\n" - " \"keyid\" (string) Public key ID, hash160 of the public key, of a public key whose BIP 32 derivation path is missing\n" - " ]\n" - " \"signatures\" : [ (array, optional)\n" - " \"keyid\" (string) Public key ID, hash160 of the public key, of a public key whose signature is missing\n" - " ]\n" - " \"redeemscript\" : \"hash\" (string, optional) Hash160 of the redeemScript that is missing\n" - " \"witnessscript\" : \"hash\" (string, optional) SHA256 of the witnessScript that is missing\n" - " }\n" - " \"next\" : \"role\" (string, optional) Role of the next person that this input needs to go to\n" - " }\n" - " ,...\n" - " ]\n" - " \"estimated_vsize\" : vsize (numeric, optional) Estimated vsize of the final signed transaction\n" - " \"estimated_feerate\" : feerate (numeric, optional) Estimated feerate of the final signed transaction in " + CURRENCY_UNIT + "/kB. Shown only if all UTXO slots in the PSBT have been filled.\n" - " \"fee\" : fee (numeric, optional) The transaction fee paid. Shown only if all UTXO slots in the PSBT have been filled.\n" - " \"next\" : \"role\" (string) Role of the next person that this psbt needs to go to\n" - " \"error\" : \"error\" (string) Error message if there is one\n" - "}\n" + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::ARR, "inputs", "", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::BOOL, "has_utxo", "Whether a UTXO is provided"}, + {RPCResult::Type::BOOL, "is_final", "Whether the input is finalized"}, + {RPCResult::Type::OBJ, "missing", /* optional */ true, "Things that are missing that are required to complete this input", + { + {RPCResult::Type::ARR, "pubkeys", /* optional */ true, "", + { + {RPCResult::Type::STR_HEX, "keyid", "Public key ID, hash160 of the public key, of a public key whose BIP 32 derivation path is missing"}, + }}, + {RPCResult::Type::ARR, "signatures", /* optional */ true, "", + { + {RPCResult::Type::STR_HEX, "keyid", "Public key ID, hash160 of the public key, of a public key whose signature is missing"}, + }}, + {RPCResult::Type::STR_HEX, "redeemscript", /* optional */ true, "Hash160 of the redeemScript that is missing"}, + {RPCResult::Type::STR_HEX, "witnessscript", /* optional */ true, "SHA256 of the witnessScript that is missing"}, + }}, + {RPCResult::Type::STR, "next", /* optional */ true, "Role of the next person that this input needs to go to"}, + }}, + }}, + {RPCResult::Type::NUM, "estimated_vsize", /* optional */ true, "Estimated vsize of the final signed transaction"}, + {RPCResult::Type::STR_AMOUNT, "estimated_feerate", /* optional */ true, "Estimated feerate of the final signed transaction in " + CURRENCY_UNIT + "/kB. Shown only if all UTXO slots in the PSBT have been filled"}, + {RPCResult::Type::STR_AMOUNT, "fee", /* optional */ true, "The transaction fee paid. Shown only if all UTXO slots in the PSBT have been filled"}, + {RPCResult::Type::STR, "next", "Role of the next person that this psbt needs to go to"}, + {RPCResult::Type::STR, "error", "Error message if there is one"}, + } }, RPCExamples { HelpExampleCli("analyzepsbt", "\"psbt\"") diff --git a/src/rpc/rawtransaction_util.cpp b/src/rpc/rawtransaction_util.cpp index 40334883c5..54baec6c6f 100644 --- a/src/rpc/rawtransaction_util.cpp +++ b/src/rpc/rawtransaction_util.cpp @@ -272,55 +272,27 @@ void SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, { int nHashType = ParseSighashString(hashType); - bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE); - // Script verification errors - UniValue vErrors(UniValue::VARR); - - // Use CTransaction for the constant parts of the - // transaction to avoid rehashing. - const CTransaction txConst(mtx); - // Sign what we can: - for (unsigned int i = 0; i < mtx.vin.size(); i++) { - CTxIn& txin = mtx.vin[i]; - auto coin = coins.find(txin.prevout); - if (coin == coins.end() || coin->second.IsSpent()) { - TxInErrorToJSON(txin, vErrors, "Input not found or already spent"); - continue; - } - const CScript& prevPubKey = coin->second.out.scriptPubKey; - const CAmount& amount = coin->second.out.nValue; - - SignatureData sigdata = DataFromTransaction(mtx, i, coin->second.out); - // Only sign SIGHASH_SINGLE if there's a corresponding output: - if (!fHashSingle || (i < mtx.vout.size())) { - ProduceSignature(*keystore, MutableTransactionSignatureCreator(&mtx, i, amount, nHashType), prevPubKey, sigdata); - } - - UpdateInput(txin, sigdata); + std::map<int, std::string> input_errors; - // amount must be specified for valid segwit signature - if (amount == MAX_MONEY && !txin.scriptWitness.IsNull()) { - throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing amount for %s", coin->second.out.ToString())); - } + bool complete = SignTransaction(mtx, keystore, coins, nHashType, input_errors); + SignTransactionResultToJSON(mtx, complete, coins, input_errors, result); +} - ScriptError serror = SCRIPT_ERR_OK; - if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), &serror)) { - if (serror == SCRIPT_ERR_INVALID_STACK_OPERATION) { - // Unable to sign input and verification failed (possible attempt to partially sign). - TxInErrorToJSON(txin, vErrors, "Unable to sign input, invalid stack size (possibly missing key)"); - } else if (serror == SCRIPT_ERR_SIG_NULLFAIL) { - // Verification failed (possibly due to insufficient signatures). - TxInErrorToJSON(txin, vErrors, "CHECK(MULTI)SIG failing with non-zero signature (possibly need more signatures)"); - } else { - TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror)); - } +void SignTransactionResultToJSON(CMutableTransaction& mtx, bool complete, const std::map<COutPoint, Coin>& coins, std::map<int, std::string>& input_errors, UniValue& result) +{ + // Make errors UniValue + UniValue vErrors(UniValue::VARR); + for (const auto& err_pair : input_errors) { + if (err_pair.second == "Missing amount") { + // This particular error needs to be an exception for some reason + throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing amount for %s", coins.at(mtx.vin.at(err_pair.first).prevout).out.ToString())); } + TxInErrorToJSON(mtx.vin.at(err_pair.first), vErrors, err_pair.second); } - bool fComplete = vErrors.empty(); result.pushKV("hex", EncodeHexTx(CTransaction(mtx))); - result.pushKV("complete", fComplete); + result.pushKV("complete", complete); if (!vErrors.empty()) { if (result.exists("errors")) { vErrors.push_backV(result["errors"].getValues()); diff --git a/src/rpc/rawtransaction_util.h b/src/rpc/rawtransaction_util.h index e70446d7f1..436db5dc60 100644 --- a/src/rpc/rawtransaction_util.h +++ b/src/rpc/rawtransaction_util.h @@ -6,6 +6,7 @@ #define BITCOIN_RPC_RAWTRANSACTION_UTIL_H #include <map> +#include <string> class FillableSigningProvider; class UniValue; @@ -24,11 +25,12 @@ class SigningProvider; * @param result JSON object where signed transaction results accumulate */ void SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, const std::map<COutPoint, Coin>& coins, const UniValue& hashType, UniValue& result); +void SignTransactionResultToJSON(CMutableTransaction& mtx, bool complete, const std::map<COutPoint, Coin>& coins, std::map<int, std::string>& input_errors, UniValue& result); /** * Parse a prevtxs UniValue array and get the map of coins from it * - * @param prevTxs Array of previous txns outputs that tx depends on but may not yet be in the block chain + * @param prevTxsUnival Array of previous txns outputs that tx depends on but may not yet be in the block chain * @param keystore A pointer to the temporary keystore if there is one * @param coins Map of unspent outputs - coins in mempool and current chain UTXO set, may be extended by previous txns outputs after call */ diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index df8e687d82..2893aa0e60 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -137,7 +137,7 @@ UniValue help(const JSONRPCRequest& jsonRequest) {"command", RPCArg::Type::STR, /* default */ "all commands", "The command to get help on"}, }, RPCResult{ - "\"text\" (string) The help text\n" + RPCResult::Type::STR, "", "The help text" }, RPCExamples{""}, }.ToString() @@ -169,7 +169,7 @@ UniValue stop(const JSONRPCRequest& jsonRequest) // this reply will get back to the client. StartShutdown(); if (jsonRequest.params[0].isNum()) { - MilliSleep(jsonRequest.params[0].get_int()); + UninterruptibleSleep(std::chrono::milliseconds{jsonRequest.params[0].get_int()}); } return PACKAGE_NAME " stopping"; } @@ -180,7 +180,7 @@ static UniValue uptime(const JSONRPCRequest& jsonRequest) "\nReturns the total uptime of the server.\n", {}, RPCResult{ - "ttt (numeric) The number of seconds that the server has been running\n" + RPCResult::Type::NUM, "", "The number of seconds that the server has been running" }, RPCExamples{ HelpExampleCli("uptime", "") @@ -197,16 +197,18 @@ static UniValue getrpcinfo(const JSONRPCRequest& request) "\nReturns details of the RPC server.\n", {}, RPCResult{ - "{\n" - " \"active_commands\" (array) All active commands\n" - " [\n" - " { (object) Information about an active command\n" - " \"method\" (string) The name of the RPC command \n" - " \"duration\" (numeric) The running time in microseconds\n" - " },...\n" - " ],\n" - " \"logpath\": \"xxx\" (string) The complete file path to the debug log\n" - "}\n" + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::ARR, "active_commands", "All active commands", + { + {RPCResult::Type::OBJ, "", "Information about an active command", + { + {RPCResult::Type::STR, "method", "The name of the RPC command"}, + {RPCResult::Type::NUM, "duration", "The running time in microseconds"}, + }}, + }}, + {RPCResult::Type::STR, "logpath", "The complete file path to the debug log"}, + } }, RPCExamples{ HelpExampleCli("getrpcinfo", "") diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp index 78586c22f9..d3081bb97f 100644 --- a/src/rpc/util.cpp +++ b/src/rpc/util.cpp @@ -14,6 +14,7 @@ #include <tuple> const std::string UNIX_EPOCH_TIME = "UNIX epoch time"; +const std::string EXAMPLE_ADDRESS[2] = {"bc1q09vm5lfy0j5reeulh4x5752q25uqqvz34hufdl", "bc1q02ad21edsxd23d32dfgqqsz4vv4nmtfzuklhy3"}; void RPCTypeCheck(const UniValue& params, const std::list<UniValueType>& typesExpected, @@ -311,20 +312,9 @@ struct Sections { } /** - * Serializing RPCArgs depends on the outer type. Only arrays and - * dictionaries can be nested in json. The top-level outer type is "named - * arguments", a mix between a dictionary and arrays. - */ - enum class OuterType { - ARR, - OBJ, - NAMED_ARG, // Only set on first recursion - }; - - /** * Recursive helper to translate an RPCArg into sections */ - void Push(const RPCArg& arg, const size_t current_indent = 5, const OuterType outer_type = OuterType::NAMED_ARG) + void Push(const RPCArg& arg, const size_t current_indent = 5, const OuterType outer_type = OuterType::NONE) { const auto indent = std::string(current_indent, ' '); const auto indent_next = std::string(current_indent + 2, ' '); @@ -337,7 +327,7 @@ struct Sections { case RPCArg::Type::AMOUNT: case RPCArg::Type::RANGE: case RPCArg::Type::BOOL: { - if (outer_type == OuterType::NAMED_ARG) return; // Nothing more to do for non-recursive types on first recursion + if (outer_type == OuterType::NONE) return; // Nothing more to do for non-recursive types on first recursion auto left = indent; if (arg.m_type_str.size() != 0 && push_name) { left += "\"" + arg.m_name + "\": " + arg.m_type_str.at(0); @@ -350,7 +340,7 @@ struct Sections { } case RPCArg::Type::OBJ: case RPCArg::Type::OBJ_USER_KEYS: { - const auto right = outer_type == OuterType::NAMED_ARG ? "" : arg.ToDescriptionString(); + const auto right = outer_type == OuterType::NONE ? "" : arg.ToDescriptionString(); PushSection({indent + (push_name ? "\"" + arg.m_name + "\": " : "") + "{", right}); for (const auto& arg_inner : arg.m_inner) { Push(arg_inner, current_indent + 2, OuterType::OBJ); @@ -358,20 +348,20 @@ struct Sections { if (arg.m_type != RPCArg::Type::OBJ) { PushSection({indent_next + "...", ""}); } - PushSection({indent + "}" + (outer_type != OuterType::NAMED_ARG ? "," : ""), ""}); + PushSection({indent + "}" + (outer_type != OuterType::NONE ? "," : ""), ""}); break; } case RPCArg::Type::ARR: { auto left = indent; left += push_name ? "\"" + arg.m_name + "\": " : ""; left += "["; - const auto right = outer_type == OuterType::NAMED_ARG ? "" : arg.ToDescriptionString(); + const auto right = outer_type == OuterType::NONE ? "" : arg.ToDescriptionString(); PushSection({left, right}); for (const auto& arg_inner : arg.m_inner) { Push(arg_inner, current_indent + 2, OuterType::ARR); } PushSection({indent_next + "...", ""}); - PushSection({indent + "]" + (outer_type != OuterType::NAMED_ARG ? "," : ""), ""}); + PushSection({indent + "]" + (outer_type != OuterType::NONE ? "," : ""), ""}); break; } @@ -443,7 +433,9 @@ std::string RPCResults::ToDescriptionString() const } else { result += "\nResult (" + r.m_cond + "):\n"; } - result += r.m_result; + Sections sections; + r.ToSections(sections); + result += sections.ToString(); } return result; } @@ -590,6 +582,93 @@ std::string RPCArg::ToDescriptionString() const return ret; } +void RPCResult::ToSections(Sections& sections, const OuterType outer_type, const int current_indent) const +{ + // Indentation + const std::string indent(current_indent, ' '); + const std::string indent_next(current_indent + 2, ' '); + + // Elements in a JSON structure (dictionary or array) are separated by a comma + const std::string maybe_separator{outer_type != OuterType::NONE ? "," : ""}; + + // The key name if recursed into an dictionary + const std::string maybe_key{ + outer_type == OuterType::OBJ ? + "\"" + this->m_key_name + "\" : " : + ""}; + + // Format description with type + const auto Description = [&](const std::string& type) { + return "(" + type + (this->m_optional ? ", optional" : "") + ")" + + (this->m_description.empty() ? "" : " " + this->m_description); + }; + + switch (m_type) { + case Type::ELISION: { + // If the inner result is empty, use three dots for elision + sections.PushSection({indent_next + "...", m_description}); + return; + } + case Type::NONE: { + sections.PushSection({indent + "None", Description("json null")}); + return; + } + case Type::STR: { + sections.PushSection({indent + maybe_key + "\"str\"" + maybe_separator, Description("string")}); + return; + } + case Type::STR_AMOUNT: { + sections.PushSection({indent + maybe_key + "n" + maybe_separator, Description("numeric")}); + return; + } + case Type::STR_HEX: { + sections.PushSection({indent + maybe_key + "\"hex\"" + maybe_separator, Description("string")}); + return; + } + case Type::NUM: { + sections.PushSection({indent + maybe_key + "n" + maybe_separator, Description("numeric")}); + return; + } + case Type::NUM_TIME: { + sections.PushSection({indent + maybe_key + "xxx" + maybe_separator, Description("numeric")}); + return; + } + case Type::BOOL: { + sections.PushSection({indent + maybe_key + "true|false" + maybe_separator, Description("boolean")}); + return; + } + case Type::ARR_FIXED: + case Type::ARR: { + sections.PushSection({indent + maybe_key + "[", Description("json array")}); + for (const auto& i : m_inner) { + i.ToSections(sections, OuterType::ARR, current_indent + 2); + } + if (m_type == Type::ARR) { + sections.PushSection({indent_next + "...", ""}); + } + sections.PushSection({indent + "]" + maybe_separator, ""}); + return; + } + case Type::OBJ_DYN: + case Type::OBJ: { + sections.PushSection({indent + maybe_key + "{", Description("json object")}); + for (const auto& i : m_inner) { + i.ToSections(sections, OuterType::OBJ, current_indent + 2); + } + if (m_type == Type::OBJ_DYN) { + // If the dictionary keys are dynamic, use three dots for continuation + sections.PushSection({indent_next + "...", ""}); + } + sections.PushSection({indent + "}" + maybe_separator, ""}); + return; + } + + // no default case, so the compiler can warn about missing cases + } + + CHECK_NONFATAL(false); +} + std::string RPCArg::ToStringObj(const bool oneline) const { std::string res; diff --git a/src/rpc/util.h b/src/rpc/util.h index 065a992a88..5813df507f 100644 --- a/src/rpc/util.h +++ b/src/rpc/util.h @@ -28,9 +28,16 @@ */ extern const std::string UNIX_EPOCH_TIME; +/** + * Example bech32 addresses for the RPCExamples help documentation. They are intentionally + * invalid to prevent accidental transactions by users. + */ +extern const std::string EXAMPLE_ADDRESS[2]; + class FillableSigningProvider; class CPubKey; class CScript; +struct Sections; /** Wrapper for UniValue::VType, which includes typeAny: * Used to denote don't care type. */ @@ -95,6 +102,16 @@ std::vector<CScript> EvalDescriptorStringOrObject(const UniValue& scanobject, Fl /** Returns, given services flags, a list of humanly readable (known) network services */ UniValue GetServicesNames(ServiceFlags services); +/** + * Serializing JSON objects depends on the outer type. Only arrays and + * dictionaries can be nested in json. The top-level outer type is "NONE". + */ +enum class OuterType { + ARR, + OBJ, + NONE, // Only set on first recursion +}; + struct RPCArg { enum class Type { OBJ, @@ -134,37 +151,37 @@ struct RPCArg { const std::vector<std::string> m_type_str; //!< Should be empty unless it is supposed to override the auto-generated type strings. Vector length is either 0 or 2, m_type_str.at(0) will override the type of the value in a key-value pair, m_type_str.at(1) will override the type in the argument description. RPCArg( - const std::string& name, - const Type& type, - const Fallback& fallback, - const std::string& description, - const std::string& oneline_description = "", - const std::vector<std::string>& type_str = {}) - : m_name{name}, - m_type{type}, - m_fallback{fallback}, - m_description{description}, - m_oneline_description{oneline_description}, - m_type_str{type_str} + const std::string name, + const Type type, + const Fallback fallback, + const std::string description, + const std::string oneline_description = "", + const std::vector<std::string> type_str = {}) + : m_name{std::move(name)}, + m_type{std::move(type)}, + m_fallback{std::move(fallback)}, + m_description{std::move(description)}, + m_oneline_description{std::move(oneline_description)}, + m_type_str{std::move(type_str)} { CHECK_NONFATAL(type != Type::ARR && type != Type::OBJ); } RPCArg( - const std::string& name, - const Type& type, - const Fallback& fallback, - const std::string& description, - const std::vector<RPCArg>& inner, - const std::string& oneline_description = "", - const std::vector<std::string>& type_str = {}) - : m_name{name}, - m_type{type}, - m_inner{inner}, - m_fallback{fallback}, - m_description{description}, - m_oneline_description{oneline_description}, - m_type_str{type_str} + const std::string name, + const Type type, + const Fallback fallback, + const std::string description, + const std::vector<RPCArg> inner, + const std::string oneline_description = "", + const std::vector<std::string> type_str = {}) + : m_name{std::move(name)}, + m_type{std::move(type)}, + m_inner{std::move(inner)}, + m_fallback{std::move(fallback)}, + m_description{std::move(description)}, + m_oneline_description{std::move(oneline_description)}, + m_type_str{std::move(type_str)} { CHECK_NONFATAL(type == Type::ARR || type == Type::OBJ); } @@ -189,21 +206,85 @@ struct RPCArg { }; struct RPCResult { + enum class Type { + OBJ, + ARR, + STR, + NUM, + BOOL, + NONE, + STR_AMOUNT, //!< Special string to represent a floating point amount + STR_HEX, //!< Special string with only hex chars + OBJ_DYN, //!< Special dictionary with keys that are not literals + ARR_FIXED, //!< Special array that has a fixed number of entries + NUM_TIME, //!< Special numeric to denote unix epoch time + ELISION, //!< Special type to denote elision (...) + }; + + const Type m_type; + const std::string m_key_name; //!< Only used for dicts + const std::vector<RPCResult> m_inner; //!< Only used for arrays or dicts + const bool m_optional; + const std::string m_description; const std::string m_cond; - const std::string m_result; - explicit RPCResult(std::string result) - : m_cond{}, m_result{std::move(result)} + RPCResult( + const std::string cond, + const Type type, + const std::string m_key_name, + const bool optional, + const std::string description, + const std::vector<RPCResult> inner = {}) + : m_type{std::move(type)}, + m_key_name{std::move(m_key_name)}, + m_inner{std::move(inner)}, + m_optional{optional}, + m_description{std::move(description)}, + m_cond{std::move(cond)} { - CHECK_NONFATAL(!m_result.empty()); + CHECK_NONFATAL(!m_cond.empty()); + const bool inner_needed{type == Type::ARR || type == Type::ARR_FIXED || type == Type::OBJ || type == Type::OBJ_DYN}; + CHECK_NONFATAL(inner_needed != inner.empty()); } - RPCResult(std::string cond, std::string result) - : m_cond{std::move(cond)}, m_result{std::move(result)} + RPCResult( + const std::string cond, + const Type type, + const std::string m_key_name, + const std::string description, + const std::vector<RPCResult> inner = {}) + : RPCResult{cond, type, m_key_name, false, description, inner} {} + + RPCResult( + const Type type, + const std::string m_key_name, + const bool optional, + const std::string description, + const std::vector<RPCResult> inner = {}) + : m_type{std::move(type)}, + m_key_name{std::move(m_key_name)}, + m_inner{std::move(inner)}, + m_optional{optional}, + m_description{std::move(description)}, + m_cond{} { - CHECK_NONFATAL(!m_cond.empty()); - CHECK_NONFATAL(!m_result.empty()); + const bool inner_needed{type == Type::ARR || type == Type::ARR_FIXED || type == Type::OBJ || type == Type::OBJ_DYN}; + CHECK_NONFATAL(inner_needed != inner.empty()); } + + RPCResult( + const Type type, + const std::string m_key_name, + const std::string description, + const std::vector<RPCResult> inner = {}) + : RPCResult{type, m_key_name, false, description, inner} {} + + /** Append the sections of the result. */ + void ToSections(Sections& sections, OuterType outer_type = OuterType::NONE, const int current_indent = 0) const; + /** Return the type string of the result when it is in an object (dict). */ + std::string ToStringObj() const; + /** Return the description string, including the result type. */ + std::string ToDescriptionString() const; }; struct RPCResults { |