aboutsummaryrefslogtreecommitdiff
path: root/src/rpc/misc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/rpc/misc.cpp')
-rw-r--r--src/rpc/misc.cpp180
1 files changed, 156 insertions, 24 deletions
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index dc3745858b..999a307e2b 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -16,6 +16,7 @@
#include <rpc/blockchain.h>
#include <rpc/server.h>
#include <rpc/util.h>
+#include <script/descriptor.h>
#include <timedata.h>
#include <util/system.h>
#include <util/strencodings.h>
@@ -39,7 +40,7 @@ static UniValue validateaddress(const JSONRPCRequest& request)
"fields have moved to getaddressinfo and will only be shown here with -deprecatedrpc=validateaddress: ismine, iswatchonly,\n"
"script, hex, pubkeys, sigsrequired, pubkey, addresses, embedded, iscompressed, account, timestamp, hdkeypath, kdmasterkeyid.\n",
{
- {"address", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The bitcoin address to validate"},
+ {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address to validate"},
},
RPCResult{
"{\n"
@@ -86,12 +87,12 @@ static UniValue createmultisig(const JSONRPCRequest& request)
"\nCreates a multi-signature address with n signature of m keys required.\n"
"It returns a json object with the address and redeemScript.\n",
{
- {"nrequired", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "The number of required signatures out of the n keys."},
- {"keys", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "A json array of hex-encoded public keys.",
+ {"nrequired", RPCArg::Type::NUM, RPCArg::Optional::NO, "The number of required signatures out of the n keys."},
+ {"keys", RPCArg::Type::ARR, RPCArg::Optional::NO, "A json array of hex-encoded public keys.",
{
- {"key", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The hex-encoded public key"},
+ {"key", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "The hex-encoded public key"},
}},
- {"address_type", RPCArg::Type::STR, /* opt */ true, /* default_val */ "legacy", "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
+ {"address_type", RPCArg::Type::STR, /* default */ "legacy", "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
},
RPCResult{
"{\n"
@@ -142,6 +143,135 @@ static UniValue createmultisig(const JSONRPCRequest& request)
return result;
}
+UniValue getdescriptorinfo(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() != 1) {
+ throw std::runtime_error(
+ RPCHelpMan{"getdescriptorinfo",
+ {"\nAnalyses a descriptor.\n"},
+ {
+ {"descriptor", RPCArg::Type::STR, RPCArg::Optional::NO, "The descriptor."},
+ },
+ RPCResult{
+ "{\n"
+ " \"descriptor\" : \"desc\", (string) The descriptor in canonical form, without private keys\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"
+ },
+ RPCExamples{
+ "Analyse a descriptor\n" +
+ HelpExampleCli("getdescriptorinfo", "\"wpkh([d34db33f/84h/0h/0h]0279be667ef9dcbbac55a06295Ce870b07029Bfcdb2dce28d959f2815b16f81798)\"")
+ }}.ToString()
+ );
+ }
+
+ RPCTypeCheck(request.params, {UniValue::VSTR});
+
+ FlatSigningProvider provider;
+ auto desc = Parse(request.params[0].get_str(), provider);
+ if (!desc) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Invalid descriptor"));
+ }
+
+ UniValue result(UniValue::VOBJ);
+ result.pushKV("descriptor", desc->ToString());
+ result.pushKV("isrange", desc->IsRange());
+ result.pushKV("issolvable", desc->IsSolvable());
+ result.pushKV("hasprivatekeys", provider.keys.size() > 0);
+ return result;
+}
+
+UniValue deriveaddresses(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.empty() || request.params.size() > 3) {
+ throw std::runtime_error(
+ RPCHelpMan{"deriveaddresses",
+ {"\nDerives one or more addresses corresponding to an output descriptor.\n"
+ "Examples of output descriptors are:\n"
+ " pkh(<pubkey>) P2PKH outputs for the given pubkey\n"
+ " wpkh(<pubkey>) Native segwit P2PKH outputs for the given pubkey\n"
+ " sh(multi(<n>,<pubkey>,<pubkey>,...)) P2SH-multisig outputs for the given threshold and pubkeys\n"
+ " raw(<hex script>) Outputs whose scriptPubKey equals the specified hex scripts\n"
+ "\nIn the above, <pubkey> either refers to a fixed public key in hexadecimal notation, or to an xpub/xprv optionally followed by one\n"
+ "or more path elements separated by \"/\", where \"h\" represents a hardened child key.\n"
+ "For more information on output descriptors, see the documentation in the doc/descriptors.md file.\n"},
+ {
+ {"descriptor", RPCArg::Type::STR, RPCArg::Optional::NO, "The descriptor."},
+ {"begin", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "If a ranged descriptor is used, this specifies the beginning of the range to import."},
+ {"end", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "If a ranged descriptor is used, this specifies the end of the range to import."}
+ },
+ RPCResult{
+ "[ address ] (array) the derived addresses\n"
+ },
+ RPCExamples{
+ "First three native segwit receive addresses\n" +
+ HelpExampleCli("deriveaddresses", "\"wpkh([d34db33f/84h/0h/0h]xpub6DJ2dNUysrn5Vt36jH2KLBT2i1auw1tTSSomg8PhqNiUtx8QX2SvC9nrHu81fT41fvDUnhMjEzQgXnQjKEu3oaqMSzhSrHMxyyoEAmUHQbY/0/*)#trd0mf0l\" 0 2")
+ }}.ToString()
+ );
+ }
+
+ RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VNUM, UniValue::VNUM});
+ const std::string desc_str = request.params[0].get_str();
+
+ int range_begin = 0;
+ int range_end = 0;
+
+ if (request.params.size() >= 2) {
+ if (request.params.size() == 2) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Missing range end parameter");
+ }
+ range_begin = request.params[1].get_int();
+ range_end = request.params[2].get_int();
+ if (range_begin < 0) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Range should be greater or equal than 0");
+ }
+ if (range_begin > range_end) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Range end should be equal to or greater than begin");
+ }
+ }
+
+ FlatSigningProvider provider;
+ auto desc = Parse(desc_str, provider, /* require_checksum = */ true);
+ if (!desc) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Invalid descriptor"));
+ }
+
+ if (!desc->IsRange() && request.params.size() > 1) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Range should not be specified for an un-ranged descriptor");
+ }
+
+ if (desc->IsRange() && request.params.size() == 1) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Range must be specified for a ranged descriptor");
+ }
+
+ UniValue addresses(UniValue::VARR);
+
+ for (int i = range_begin; i <= range_end; ++i) {
+ std::vector<CScript> scripts;
+ if (!desc->Expand(i, provider, scripts, provider)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Cannot derive script without private keys"));
+ }
+
+ for (const CScript &script : scripts) {
+ CTxDestination dest;
+ if (!ExtractDestination(script, dest)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Descriptor does not have a corresponding address"));
+ }
+
+ addresses.push_back(EncodeDestination(dest));
+ }
+ }
+
+ // This should not be possible, but an assert seems overkill:
+ if (addresses.empty()) {
+ throw JSONRPCError(RPC_MISC_ERROR, "Unexpected empty result");
+ }
+
+ return addresses;
+}
+
static UniValue verifymessage(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 3)
@@ -149,9 +279,9 @@ static UniValue verifymessage(const JSONRPCRequest& request)
RPCHelpMan{"verifymessage",
"\nVerify a signed message\n",
{
- {"address", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The bitcoin address to use for the signature."},
- {"signature", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The signature provided by the signer in base 64 encoding (see signmessage)."},
- {"message", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The message that was signed."},
+ {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address to use for the signature."},
+ {"signature", RPCArg::Type::STR, RPCArg::Optional::NO, "The signature provided by the signer in base 64 encoding (see signmessage)."},
+ {"message", RPCArg::Type::STR, RPCArg::Optional::NO, "The message that was signed."},
},
RPCResult{
"true|false (boolean) If the signature is verified or not.\n"
@@ -208,8 +338,8 @@ static UniValue signmessagewithprivkey(const JSONRPCRequest& request)
RPCHelpMan{"signmessagewithprivkey",
"\nSign a message with the private key of an address\n",
{
- {"privkey", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The private key to sign the message with."},
- {"message", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The message to create a signature of."},
+ {"privkey", RPCArg::Type::STR, RPCArg::Optional::NO, "The private key to sign the message with."},
+ {"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"
@@ -250,7 +380,7 @@ static UniValue setmocktime(const JSONRPCRequest& request)
RPCHelpMan{"setmocktime",
"\nSet the local time to given timestamp (-regtest only)\n",
{
- {"timestamp", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "Unix seconds-since-epoch timestamp\n"
+ {"timestamp", RPCArg::Type::NUM, RPCArg::Optional::NO, "Unix seconds-since-epoch timestamp\n"
" Pass 0 to go back to using the system time."},
},
RPCResults{},
@@ -316,7 +446,7 @@ static UniValue getmemoryinfo(const JSONRPCRequest& request)
RPCHelpMan{"getmemoryinfo",
"Returns an object containing information about memory usage.\n",
{
- {"mode", RPCArg::Type::STR, /* opt */ true, /* default_val */ "\"stats\"", "determines what kind of information is returned.\n"
+ {"mode", RPCArg::Type::STR, /* default */ "\"stats\"", "determines what kind of information is returned.\n"
" - \"stats\" returns general statistics about memory usage in the daemon.\n"
" - \"mallocinfo\" returns an XML string describing low-level heap state (only available if compiled with glibc 2.10+)."},
},
@@ -366,9 +496,9 @@ static void EnableOrDisableLogCategories(UniValue cats, bool enable) {
bool success;
if (enable) {
- success = g_logger->EnableCategory(cat);
+ success = LogInstance().EnableCategory(cat);
} else {
- success = g_logger->DisableCategory(cat);
+ success = LogInstance().DisableCategory(cat);
}
if (!success) {
@@ -393,36 +523,36 @@ UniValue logging(const JSONRPCRequest& request)
" - \"none\", \"0\" : even if other logging categories are specified, ignore all of them.\n"
,
{
- {"include", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "null", "A json array of categories to add debug logging",
+ {"include", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "A json array of categories to add debug logging",
{
- {"include_category", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "the valid logging category"},
+ {"include_category", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "the valid logging category"},
}},
- {"exclude", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "null", "A json array of categories to remove debug logging",
+ {"exclude", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "A json array of categories to remove debug logging",
{
- {"exclude_category", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "the valid logging category"},
+ {"exclude_category", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "the valid logging category"},
}},
},
RPCResult{
"{ (json object where keys are the logging categories, and values indicates its status\n"
- " \"category\": 0|1, (numeric) if being debug logged or not. 0:inactive, 1:active\n"
+ " \"category\": true|false, (bool) if being debug logged or not. false:inactive, true:active\n"
" ...\n"
"}\n"
},
RPCExamples{
HelpExampleCli("logging", "\"[\\\"all\\\"]\" \"[\\\"http\\\"]\"")
- + HelpExampleRpc("logging", "[\"all\"], \"[libevent]\"")
+ + HelpExampleRpc("logging", "[\"all\"], [\"libevent\"]")
},
}.ToString());
}
- uint32_t original_log_categories = g_logger->GetCategoryMask();
+ uint32_t original_log_categories = LogInstance().GetCategoryMask();
if (request.params[0].isArray()) {
EnableOrDisableLogCategories(request.params[0], true);
}
if (request.params[1].isArray()) {
EnableOrDisableLogCategories(request.params[1], false);
}
- uint32_t updated_log_categories = g_logger->GetCategoryMask();
+ uint32_t updated_log_categories = LogInstance().GetCategoryMask();
uint32_t changed_log_categories = original_log_categories ^ updated_log_categories;
// Update libevent logging if BCLog::LIBEVENT has changed.
@@ -431,8 +561,8 @@ UniValue logging(const JSONRPCRequest& request)
// Throw an error if the user has explicitly asked to change only the libevent
// flag and it failed.
if (changed_log_categories & BCLog::LIBEVENT) {
- if (!UpdateHTTPServerLogging(g_logger->WillLogCategory(BCLog::LIBEVENT))) {
- g_logger->DisableCategory(BCLog::LIBEVENT);
+ if (!UpdateHTTPServerLogging(LogInstance().WillLogCategory(BCLog::LIBEVENT))) {
+ LogInstance().DisableCategory(BCLog::LIBEVENT);
if (changed_log_categories == BCLog::LIBEVENT) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "libevent logging cannot be updated when using libevent before v2.1.1.");
}
@@ -473,6 +603,8 @@ static const CRPCCommand commands[] =
{ "control", "logging", &logging, {"include", "exclude"}},
{ "util", "validateaddress", &validateaddress, {"address"} },
{ "util", "createmultisig", &createmultisig, {"nrequired","keys","address_type"} },
+ { "util", "deriveaddresses", &deriveaddresses, {"descriptor", "begin", "end"} },
+ { "util", "getdescriptorinfo", &getdescriptorinfo, {"descriptor"} },
{ "util", "verifymessage", &verifymessage, {"address","signature","message"} },
{ "util", "signmessagewithprivkey", &signmessagewithprivkey, {"privkey","message"} },