Changeset View
Changeset View
Standalone View
Standalone View
src/rpc/misc.cpp
Show All 25 Lines | |||||
#include <univalue.h> | #include <univalue.h> | ||||
#include <cstdint> | #include <cstdint> | ||||
#include <tuple> | #include <tuple> | ||||
#ifdef HAVE_MALLOC_INFO | #ifdef HAVE_MALLOC_INFO | ||||
#include <malloc.h> | #include <malloc.h> | ||||
#endif | #endif | ||||
static UniValue validateaddress(const Config &config, | static RPCHelpMan validateaddress() { | ||||
const JSONRPCRequest &request) { | return RPCHelpMan{ | ||||
RPCHelpMan{ | |||||
"validateaddress", | "validateaddress", | ||||
"Return information about the given bitcoin address.\n", | "Return information about the given bitcoin address.\n", | ||||
{ | { | ||||
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, | {"address", RPCArg::Type::STR, RPCArg::Optional::NO, | ||||
"The bitcoin address to validate"}, | "The bitcoin address to validate"}, | ||||
}, | }, | ||||
RPCResult{ | RPCResult{ | ||||
RPCResult::Type::OBJ, | RPCResult::Type::OBJ, | ||||
"", | "", | ||||
"", | "", | ||||
{ | { | ||||
{RPCResult::Type::BOOL, "isvalid", | {RPCResult::Type::BOOL, "isvalid", | ||||
"If the address is valid or not. If not, this is the only " | "If the address is valid or not. If not, this is the only " | ||||
"property returned."}, | "property returned."}, | ||||
{RPCResult::Type::STR, "address", | {RPCResult::Type::STR, "address", | ||||
"The bitcoin address validated"}, | "The bitcoin address validated"}, | ||||
{RPCResult::Type::STR_HEX, "scriptPubKey", | {RPCResult::Type::STR_HEX, "scriptPubKey", | ||||
"The hex-encoded scriptPubKey generated by the address"}, | "The hex-encoded scriptPubKey generated by the address"}, | ||||
{RPCResult::Type::BOOL, "isscript", "If the key is a script"}, | {RPCResult::Type::BOOL, "isscript", "If the key is a script"}, | ||||
}}, | }}, | ||||
RPCExamples{HelpExampleCli("validateaddress", EXAMPLE_ADDRESS) + | RPCExamples{HelpExampleCli("validateaddress", EXAMPLE_ADDRESS) + | ||||
HelpExampleRpc("validateaddress", EXAMPLE_ADDRESS)}, | HelpExampleRpc("validateaddress", EXAMPLE_ADDRESS)}, | ||||
} | [&](const RPCHelpMan &self, Config &config, | ||||
.Check(request); | const JSONRPCRequest &request) -> UniValue { | ||||
CTxDestination dest = DecodeDestination(request.params[0].get_str(), | |||||
CTxDestination dest = | config.GetChainParams()); | ||||
DecodeDestination(request.params[0].get_str(), config.GetChainParams()); | |||||
bool isValid = IsValidDestination(dest); | bool isValid = IsValidDestination(dest); | ||||
UniValue ret(UniValue::VOBJ); | UniValue ret(UniValue::VOBJ); | ||||
ret.pushKV("isvalid", isValid); | ret.pushKV("isvalid", isValid); | ||||
if (isValid) { | if (isValid) { | ||||
if (ret["address"].isNull()) { | if (ret["address"].isNull()) { | ||||
std::string currentAddress = EncodeDestination(dest, config); | std::string currentAddress = | ||||
EncodeDestination(dest, config); | |||||
ret.pushKV("address", currentAddress); | ret.pushKV("address", currentAddress); | ||||
CScript scriptPubKey = GetScriptForDestination(dest); | CScript scriptPubKey = GetScriptForDestination(dest); | ||||
ret.pushKV("scriptPubKey", HexStr(scriptPubKey)); | ret.pushKV("scriptPubKey", HexStr(scriptPubKey)); | ||||
UniValue detail = DescribeAddress(dest); | UniValue detail = DescribeAddress(dest); | ||||
ret.pushKVs(detail); | ret.pushKVs(detail); | ||||
} | } | ||||
} | } | ||||
return ret; | return ret; | ||||
}, | |||||
}; | |||||
} | } | ||||
static UniValue createmultisig(const Config &config, | static RPCHelpMan createmultisig() { | ||||
const JSONRPCRequest &request) { | return RPCHelpMan{ | ||||
RPCHelpMan{ | |||||
"createmultisig", | "createmultisig", | ||||
"Creates a multi-signature address with n signature of m keys " | "Creates a multi-signature address with n signature of m keys " | ||||
"required.\n" | "required.\n" | ||||
"It returns a json object with the address and redeemScript.\n", | "It returns a json object with the address and redeemScript.\n", | ||||
{ | { | ||||
{"nrequired", RPCArg::Type::NUM, RPCArg::Optional::NO, | {"nrequired", RPCArg::Type::NUM, RPCArg::Optional::NO, | ||||
"The number of required signatures out of the n keys."}, | "The number of required signatures out of the n keys."}, | ||||
{"keys", | {"keys", | ||||
Show All 29 Lines | return RPCHelpMan{ | ||||
"\nAs a JSON-RPC call\n" + | "\nAs a JSON-RPC call\n" + | ||||
HelpExampleRpc("createmultisig", | HelpExampleRpc("createmultisig", | ||||
"2, " | "2, " | ||||
"\"[" | "\"[" | ||||
"\\\"03789ed0bb717d88f7d321a368d905e7430207ebbd82bd3" | "\\\"03789ed0bb717d88f7d321a368d905e7430207ebbd82bd3" | ||||
"42cf11ae157a7ace5fd\\\"," | "42cf11ae157a7ace5fd\\\"," | ||||
"\\\"03dbc6764b8884a92e871274b87583e6d5c2a58819473e1" | "\\\"03dbc6764b8884a92e871274b87583e6d5c2a58819473e1" | ||||
"7e107ef3f6aa5a61626\\\"]\"")}, | "7e107ef3f6aa5a61626\\\"]\"")}, | ||||
} | [&](const RPCHelpMan &self, Config &config, | ||||
.Check(request); | const JSONRPCRequest &request) -> UniValue { | ||||
int required = request.params[0].get_int(); | int required = request.params[0].get_int(); | ||||
// Get the public keys | // Get the public keys | ||||
const UniValue &keys = request.params[1].get_array(); | const UniValue &keys = request.params[1].get_array(); | ||||
std::vector<CPubKey> pubkeys; | std::vector<CPubKey> pubkeys; | ||||
for (size_t i = 0; i < keys.size(); ++i) { | for (size_t i = 0; i < keys.size(); ++i) { | ||||
if ((keys[i].get_str().length() == 2 * CPubKey::COMPRESSED_SIZE || | if ((keys[i].get_str().length() == | ||||
2 * CPubKey::COMPRESSED_SIZE || | |||||
keys[i].get_str().length() == 2 * CPubKey::SIZE) && | keys[i].get_str().length() == 2 * CPubKey::SIZE) && | ||||
IsHex(keys[i].get_str())) { | IsHex(keys[i].get_str())) { | ||||
pubkeys.push_back(HexToPubKey(keys[i].get_str())); | pubkeys.push_back(HexToPubKey(keys[i].get_str())); | ||||
} else { | } else { | ||||
throw JSONRPCError( | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | ||||
RPC_INVALID_ADDRESS_OR_KEY, | strprintf("Invalid public key: %s\n", | ||||
strprintf("Invalid public key: %s\n", keys[i].get_str())); | keys[i].get_str())); | ||||
} | } | ||||
} | } | ||||
// Get the output type | // Get the output type | ||||
OutputType output_type = OutputType::LEGACY; | OutputType output_type = OutputType::LEGACY; | ||||
// Construct using pay-to-script-hash: | // Construct using pay-to-script-hash: | ||||
FillableSigningProvider keystore; | FillableSigningProvider keystore; | ||||
CScript inner; | CScript inner; | ||||
const CTxDestination dest = AddAndGetMultisigDestination( | const CTxDestination dest = AddAndGetMultisigDestination( | ||||
required, pubkeys, output_type, keystore, inner); | required, pubkeys, output_type, keystore, inner); | ||||
// Make the descriptor | // Make the descriptor | ||||
std::unique_ptr<Descriptor> descriptor = | std::unique_ptr<Descriptor> descriptor = | ||||
InferDescriptor(GetScriptForDestination(dest), keystore); | InferDescriptor(GetScriptForDestination(dest), keystore); | ||||
UniValue result(UniValue::VOBJ); | UniValue result(UniValue::VOBJ); | ||||
result.pushKV("address", EncodeDestination(dest, config)); | result.pushKV("address", EncodeDestination(dest, config)); | ||||
result.pushKV("redeemScript", HexStr(inner)); | result.pushKV("redeemScript", HexStr(inner)); | ||||
result.pushKV("descriptor", descriptor->ToString()); | result.pushKV("descriptor", descriptor->ToString()); | ||||
return result; | return result; | ||||
}, | |||||
}; | |||||
} | } | ||||
UniValue getdescriptorinfo(const Config &config, | static RPCHelpMan getdescriptorinfo() { | ||||
const JSONRPCRequest &request) { | return RPCHelpMan{ | ||||
RPCHelpMan{ | |||||
"getdescriptorinfo", | "getdescriptorinfo", | ||||
{"Analyses a descriptor.\n"}, | {"Analyses a descriptor.\n"}, | ||||
{ | { | ||||
{"descriptor", RPCArg::Type::STR, RPCArg::Optional::NO, | {"descriptor", RPCArg::Type::STR, RPCArg::Optional::NO, | ||||
"The descriptor."}, | "The descriptor."}, | ||||
}, | }, | ||||
RPCResult{ | RPCResult{ | ||||
RPCResult::Type::OBJ, | RPCResult::Type::OBJ, | ||||
Show All 12 Lines | return RPCHelpMan{ | ||||
"Whether the input descriptor contained at least one private " | "Whether the input descriptor contained at least one private " | ||||
"key"}, | "key"}, | ||||
}}, | }}, | ||||
RPCExamples{"Analyse a descriptor\n" + | RPCExamples{"Analyse a descriptor\n" + | ||||
HelpExampleCli("getdescriptorinfo", | HelpExampleCli("getdescriptorinfo", | ||||
"\"pkh([d34db33f/84h/0h/" | "\"pkh([d34db33f/84h/0h/" | ||||
"0h]" | "0h]" | ||||
"0279be667ef9dcbbac55a06295Ce870b07029Bfcdb2" | "0279be667ef9dcbbac55a06295Ce870b07029Bfcdb2" | ||||
"dce28d959f2815b16f81798)\"")}} | "dce28d959f2815b16f81798)\"")}, | ||||
.Check(request); | [&](const RPCHelpMan &self, Config &config, | ||||
const JSONRPCRequest &request) -> UniValue { | |||||
RPCTypeCheck(request.params, {UniValue::VSTR}); | RPCTypeCheck(request.params, {UniValue::VSTR}); | ||||
FlatSigningProvider provider; | FlatSigningProvider provider; | ||||
std::string error; | std::string error; | ||||
auto desc = Parse(request.params[0].get_str(), provider, error); | auto desc = Parse(request.params[0].get_str(), provider, error); | ||||
if (!desc) { | if (!desc) { | ||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error); | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error); | ||||
} | } | ||||
UniValue result(UniValue::VOBJ); | UniValue result(UniValue::VOBJ); | ||||
result.pushKV("descriptor", desc->ToString()); | result.pushKV("descriptor", desc->ToString()); | ||||
result.pushKV("checksum", | result.pushKV("checksum", | ||||
GetDescriptorChecksum(request.params[0].get_str())); | GetDescriptorChecksum(request.params[0].get_str())); | ||||
result.pushKV("isrange", desc->IsRange()); | result.pushKV("isrange", desc->IsRange()); | ||||
result.pushKV("issolvable", desc->IsSolvable()); | result.pushKV("issolvable", desc->IsSolvable()); | ||||
result.pushKV("hasprivatekeys", provider.keys.size() > 0); | result.pushKV("hasprivatekeys", provider.keys.size() > 0); | ||||
return result; | return result; | ||||
}, | |||||
}; | |||||
} | } | ||||
UniValue deriveaddresses(const Config &config, const JSONRPCRequest &request) { | static RPCHelpMan deriveaddresses() { | ||||
RPCHelpMan{ | return RPCHelpMan{ | ||||
"deriveaddresses", | "deriveaddresses", | ||||
{"Derives one or more addresses corresponding to an output " | {"Derives one or more addresses corresponding to an output " | ||||
"descriptor.\n" | "descriptor.\n" | ||||
"Examples of output descriptors are:\n" | "Examples of output descriptors are:\n" | ||||
" pkh(<pubkey>) P2PKH outputs for the given " | " pkh(<pubkey>) P2PKH outputs for the given " | ||||
"pubkey\n" | "pubkey\n" | ||||
" sh(multi(<n>,<pubkey>,<pubkey>,...)) P2SH-multisig outputs for " | " sh(multi(<n>,<pubkey>,<pubkey>,...)) P2SH-multisig outputs for " | ||||
"the given threshold and pubkeys\n" | "the given threshold and pubkeys\n" | ||||
Show All 20 Lines | return RPCHelpMan{ | ||||
{RPCResult::Type::STR, "address", "the derived addresses"}, | {RPCResult::Type::STR, "address", "the derived addresses"}, | ||||
}}, | }}, | ||||
RPCExamples{"First three pkh receive addresses\n" + | RPCExamples{"First three pkh receive addresses\n" + | ||||
HelpExampleCli( | HelpExampleCli( | ||||
"deriveaddresses", | "deriveaddresses", | ||||
"\"pkh([d34db33f/84h/0h/0h]" | "\"pkh([d34db33f/84h/0h/0h]" | ||||
"xpub6DJ2dNUysrn5Vt36jH2KLBT2i1auw1tTSSomg8P" | "xpub6DJ2dNUysrn5Vt36jH2KLBT2i1auw1tTSSomg8P" | ||||
"hqNiUtx8QX2SvC9nrHu81fT41fvDUnhMjEzQgXnQjKE" | "hqNiUtx8QX2SvC9nrHu81fT41fvDUnhMjEzQgXnQjKE" | ||||
"u3oaqMSzhSrHMxyyoEAmUHQbY/0/*)#3vhfv5h5\" \"[0,2]\"")}} | "u3oaqMSzhSrHMxyyoEAmUHQbY/0/*)#3vhfv5h5\" \"[0,2]\"")}, | ||||
.Check(request); | [&](const RPCHelpMan &self, Config &config, | ||||
const JSONRPCRequest &request) -> UniValue { | |||||
// Range argument is checked later | // Range argument is checked later | ||||
RPCTypeCheck(request.params, {UniValue::VSTR, UniValueType()}); | RPCTypeCheck(request.params, {UniValue::VSTR, UniValueType()}); | ||||
const std::string desc_str = request.params[0].get_str(); | const std::string desc_str = request.params[0].get_str(); | ||||
int64_t range_begin = 0; | int64_t range_begin = 0; | ||||
int64_t range_end = 0; | int64_t range_end = 0; | ||||
if (request.params.size() >= 2 && !request.params[1].isNull()) { | if (request.params.size() >= 2 && !request.params[1].isNull()) { | ||||
std::tie(range_begin, range_end) = | std::tie(range_begin, range_end) = | ||||
ParseDescriptorRange(request.params[1]); | ParseDescriptorRange(request.params[1]); | ||||
} | } | ||||
FlatSigningProvider key_provider; | FlatSigningProvider key_provider; | ||||
std::string error; | std::string error; | ||||
auto desc = | auto desc = Parse(desc_str, key_provider, error, | ||||
Parse(desc_str, key_provider, error, /* require_checksum = */ true); | /* require_checksum = */ true); | ||||
if (!desc) { | if (!desc) { | ||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error); | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error); | ||||
} | } | ||||
if (!desc->IsRange() && request.params.size() > 1) { | if (!desc->IsRange() && request.params.size() > 1) { | ||||
throw JSONRPCError( | throw JSONRPCError(RPC_INVALID_PARAMETER, | ||||
RPC_INVALID_PARAMETER, | "Range should not be specified for an " | ||||
"Range should not be specified for an un-ranged descriptor"); | "un-ranged descriptor"); | ||||
} | } | ||||
if (desc->IsRange() && request.params.size() == 1) { | if (desc->IsRange() && request.params.size() == 1) { | ||||
throw JSONRPCError(RPC_INVALID_PARAMETER, | throw JSONRPCError( | ||||
RPC_INVALID_PARAMETER, | |||||
"Range must be specified for a ranged descriptor"); | "Range must be specified for a ranged descriptor"); | ||||
} | } | ||||
UniValue addresses(UniValue::VARR); | UniValue addresses(UniValue::VARR); | ||||
for (int i = range_begin; i <= range_end; ++i) { | for (int i = range_begin; i <= range_end; ++i) { | ||||
FlatSigningProvider provider; | FlatSigningProvider provider; | ||||
std::vector<CScript> scripts; | std::vector<CScript> scripts; | ||||
if (!desc->Expand(i, key_provider, scripts, provider)) { | if (!desc->Expand(i, key_provider, scripts, provider)) { | ||||
throw JSONRPCError( | throw JSONRPCError( | ||||
RPC_INVALID_ADDRESS_OR_KEY, | RPC_INVALID_ADDRESS_OR_KEY, | ||||
strprintf("Cannot derive script without private keys")); | strprintf("Cannot derive script without private keys")); | ||||
} | } | ||||
for (const CScript &script : scripts) { | for (const CScript &script : scripts) { | ||||
CTxDestination dest; | CTxDestination dest; | ||||
if (!ExtractDestination(script, dest)) { | if (!ExtractDestination(script, dest)) { | ||||
throw JSONRPCError( | throw JSONRPCError( | ||||
RPC_INVALID_ADDRESS_OR_KEY, | RPC_INVALID_ADDRESS_OR_KEY, | ||||
strprintf( | strprintf("Descriptor does not have a " | ||||
"Descriptor does not have a corresponding address")); | "corresponding address")); | ||||
} | } | ||||
addresses.push_back(EncodeDestination(dest, config)); | addresses.push_back(EncodeDestination(dest, config)); | ||||
} | } | ||||
} | } | ||||
// This should not be possible, but an assert seems overkill: | // This should not be possible, but an assert seems overkill: | ||||
if (addresses.empty()) { | if (addresses.empty()) { | ||||
throw JSONRPCError(RPC_MISC_ERROR, "Unexpected empty result"); | throw JSONRPCError(RPC_MISC_ERROR, "Unexpected empty result"); | ||||
} | } | ||||
return addresses; | return addresses; | ||||
}, | |||||
}; | |||||
} | } | ||||
static UniValue verifymessage(const Config &config, | static RPCHelpMan verifymessage() { | ||||
const JSONRPCRequest &request) { | return RPCHelpMan{ | ||||
RPCHelpMan{ | |||||
"verifymessage", | "verifymessage", | ||||
"Verify a signed message\n", | "Verify a signed message\n", | ||||
{ | { | ||||
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, | {"address", RPCArg::Type::STR, RPCArg::Optional::NO, | ||||
"The bitcoin address to use for the signature."}, | "The bitcoin address to use for the signature."}, | ||||
{"signature", RPCArg::Type::STR, RPCArg::Optional::NO, | {"signature", RPCArg::Type::STR, RPCArg::Optional::NO, | ||||
"The signature provided by the signer in base 64 encoding (see " | "The signature provided by the signer in base 64 encoding (see " | ||||
"signmessage)."}, | "signmessage)."}, | ||||
Show All 12 Lines | return RPCHelpMan{ | ||||
"\nVerify the signature\n" + | "\nVerify the signature\n" + | ||||
HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4" | HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4" | ||||
"XX\" \"signature\" \"my " | "XX\" \"signature\" \"my " | ||||
"message\"") + | "message\"") + | ||||
"\nAs a JSON-RPC call\n" + | "\nAs a JSON-RPC call\n" + | ||||
HelpExampleRpc("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4" | HelpExampleRpc("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4" | ||||
"XX\", \"signature\", \"my " | "XX\", \"signature\", \"my " | ||||
"message\"")}, | "message\"")}, | ||||
} | [&](const RPCHelpMan &self, Config &config, | ||||
.Check(request); | const JSONRPCRequest &request) -> UniValue { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
std::string strAddress = request.params[0].get_str(); | std::string strAddress = request.params[0].get_str(); | ||||
std::string strSign = request.params[1].get_str(); | std::string strSign = request.params[1].get_str(); | ||||
std::string strMessage = request.params[2].get_str(); | std::string strMessage = request.params[2].get_str(); | ||||
switch (MessageVerify(config.GetChainParams(), strAddress, strSign, | switch (MessageVerify(config.GetChainParams(), strAddress, strSign, | ||||
strMessage)) { | strMessage)) { | ||||
case MessageVerificationResult::ERR_INVALID_ADDRESS: | case MessageVerificationResult::ERR_INVALID_ADDRESS: | ||||
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address"); | throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address"); | ||||
case MessageVerificationResult::ERR_ADDRESS_NO_KEY: | case MessageVerificationResult::ERR_ADDRESS_NO_KEY: | ||||
throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); | throw JSONRPCError(RPC_TYPE_ERROR, | ||||
"Address does not refer to key"); | |||||
case MessageVerificationResult::ERR_MALFORMED_SIGNATURE: | case MessageVerificationResult::ERR_MALFORMED_SIGNATURE: | ||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | ||||
"Malformed base64 encoding"); | "Malformed base64 encoding"); | ||||
case MessageVerificationResult::ERR_PUBKEY_NOT_RECOVERED: | case MessageVerificationResult::ERR_PUBKEY_NOT_RECOVERED: | ||||
case MessageVerificationResult::ERR_NOT_SIGNED: | case MessageVerificationResult::ERR_NOT_SIGNED: | ||||
return false; | return false; | ||||
case MessageVerificationResult::OK: | case MessageVerificationResult::OK: | ||||
return true; | return true; | ||||
} | } | ||||
return false; | return false; | ||||
}, | |||||
}; | |||||
} | } | ||||
static UniValue signmessagewithprivkey(const Config &config, | static RPCHelpMan signmessagewithprivkey() { | ||||
const JSONRPCRequest &request) { | return RPCHelpMan{ | ||||
RPCHelpMan{ | |||||
"signmessagewithprivkey", | "signmessagewithprivkey", | ||||
"Sign a message with the private key of an address\n", | "Sign a message with the private key of an address\n", | ||||
{ | { | ||||
{"privkey", RPCArg::Type::STR, RPCArg::Optional::NO, | {"privkey", RPCArg::Type::STR, RPCArg::Optional::NO, | ||||
"The private key to sign the message with."}, | "The private key to sign the message with."}, | ||||
{"message", RPCArg::Type::STR, RPCArg::Optional::NO, | {"message", RPCArg::Type::STR, RPCArg::Optional::NO, | ||||
"The message to create a signature of."}, | "The message to create a signature of."}, | ||||
}, | }, | ||||
RPCResult{RPCResult::Type::STR, "signature", | RPCResult{RPCResult::Type::STR, "signature", | ||||
"The signature of the message encoded in base 64"}, | "The signature of the message encoded in base 64"}, | ||||
RPCExamples{"\nCreate the signature\n" + | RPCExamples{"\nCreate the signature\n" + | ||||
HelpExampleCli("signmessagewithprivkey", | HelpExampleCli("signmessagewithprivkey", | ||||
"\"privkey\" \"my message\"") + | "\"privkey\" \"my message\"") + | ||||
"\nVerify the signature\n" + | "\nVerify the signature\n" + | ||||
HelpExampleCli("verifymessage", | HelpExampleCli("verifymessage", | ||||
"\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" " | "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" " | ||||
"\"signature\" \"my message\"") + | "\"signature\" \"my message\"") + | ||||
"\nAs a JSON-RPC call\n" + | "\nAs a JSON-RPC call\n" + | ||||
HelpExampleRpc("signmessagewithprivkey", | HelpExampleRpc("signmessagewithprivkey", | ||||
"\"privkey\", \"my message\"")}, | "\"privkey\", \"my message\"")}, | ||||
} | [&](const RPCHelpMan &self, Config &config, | ||||
.Check(request); | const JSONRPCRequest &request) -> UniValue { | ||||
std::string strPrivkey = request.params[0].get_str(); | std::string strPrivkey = request.params[0].get_str(); | ||||
std::string strMessage = request.params[1].get_str(); | std::string strMessage = request.params[1].get_str(); | ||||
CKey key = DecodeSecret(strPrivkey); | CKey key = DecodeSecret(strPrivkey); | ||||
if (!key.IsValid()) { | if (!key.IsValid()) { | ||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key"); | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | ||||
"Invalid private key"); | |||||
} | } | ||||
std::string signature; | std::string signature; | ||||
if (!MessageSign(key, strMessage, signature)) { | if (!MessageSign(key, strMessage, signature)) { | ||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed"); | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed"); | ||||
} | } | ||||
return signature; | return signature; | ||||
}, | |||||
}; | |||||
} | } | ||||
static UniValue setmocktime(const Config &config, | static RPCHelpMan setmocktime() { | ||||
const JSONRPCRequest &request) { | return RPCHelpMan{ | ||||
RPCHelpMan{ | |||||
"setmocktime", | "setmocktime", | ||||
"Set the local time to given timestamp (-regtest only)\n", | "Set the local time to given timestamp (-regtest only)\n", | ||||
{ | { | ||||
{"timestamp", RPCArg::Type::NUM, RPCArg::Optional::NO, | {"timestamp", RPCArg::Type::NUM, RPCArg::Optional::NO, | ||||
UNIX_EPOCH_TIME + | UNIX_EPOCH_TIME + | ||||
"\n" | "\n" | ||||
" Pass 0 to go back to using the system time."}, | " Pass 0 to go back to using the system time."}, | ||||
}, | }, | ||||
RPCResult{RPCResult::Type::NONE, "", ""}, | RPCResult{RPCResult::Type::NONE, "", ""}, | ||||
RPCExamples{""}, | RPCExamples{""}, | ||||
} | [&](const RPCHelpMan &self, Config &config, | ||||
.Check(request); | const JSONRPCRequest &request) -> UniValue { | ||||
if (!config.GetChainParams().IsMockableChain()) { | if (!config.GetChainParams().IsMockableChain()) { | ||||
throw std::runtime_error( | throw std::runtime_error( | ||||
"setmocktime for regression testing (-regtest mode) only"); | "setmocktime for regression testing (-regtest mode) only"); | ||||
} | } | ||||
// For now, don't change mocktime if we're in the middle of validation, as | // For now, don't change mocktime if we're in the middle of | ||||
// this could have an effect on mempool time-based eviction, as well as | // validation, as this could have an effect on mempool time-based | ||||
// IsInitialBlockDownload(). | // eviction, as well as IsInitialBlockDownload(). | ||||
// TODO: figure out the right way to synchronize around mocktime, and | // TODO: figure out the right way to synchronize around mocktime, | ||||
// ensure all call sites of GetTime() are accessing this safely. | // and ensure all call sites of GetTime() are accessing this safely. | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
RPCTypeCheck(request.params, {UniValue::VNUM}); | RPCTypeCheck(request.params, {UniValue::VNUM}); | ||||
int64_t time = request.params[0].get_int64(); | int64_t time = request.params[0].get_int64(); | ||||
if (time < 0) { | if (time < 0) { | ||||
throw JSONRPCError(RPC_INVALID_PARAMETER, | throw JSONRPCError(RPC_INVALID_PARAMETER, | ||||
"Timestamp must be 0 or greater"); | "Timestamp must be 0 or greater"); | ||||
} | } | ||||
SetMockTime(time); | SetMockTime(time); | ||||
if (request.context.Has<NodeContext>()) { | if (request.context.Has<NodeContext>()) { | ||||
for (const auto &chain_client : | for (const auto &chain_client : | ||||
request.context.Get<NodeContext>().chain_clients) { | request.context.Get<NodeContext>().chain_clients) { | ||||
chain_client->setMockTime(time); | chain_client->setMockTime(time); | ||||
} | } | ||||
} | } | ||||
return NullUniValue; | return NullUniValue; | ||||
}, | |||||
}; | |||||
} | } | ||||
static UniValue mockscheduler(const Config &config, | static RPCHelpMan mockscheduler() { | ||||
const JSONRPCRequest &request) { | return RPCHelpMan{ | ||||
RPCHelpMan{ | |||||
"mockscheduler", | "mockscheduler", | ||||
"Bump the scheduler into the future (-regtest only)\n", | "Bump the scheduler into the future (-regtest only)\n", | ||||
{ | { | ||||
{"delta_time", RPCArg::Type::NUM, RPCArg::Optional::NO, | {"delta_time", RPCArg::Type::NUM, RPCArg::Optional::NO, | ||||
"Number of seconds to forward the scheduler into the future."}, | "Number of seconds to forward the scheduler into the future."}, | ||||
}, | }, | ||||
RPCResult{RPCResult::Type::NONE, "", ""}, | RPCResult{RPCResult::Type::NONE, "", ""}, | ||||
RPCExamples{""}, | RPCExamples{""}, | ||||
} | [&](const RPCHelpMan &self, Config &config, | ||||
.Check(request); | const JSONRPCRequest &request) -> UniValue { | ||||
if (!Params().IsMockableChain()) { | if (!Params().IsMockableChain()) { | ||||
throw std::runtime_error( | throw std::runtime_error("mockscheduler is for regression " | ||||
"mockscheduler is for regression testing (-regtest mode) only"); | "testing (-regtest mode) only"); | ||||
} | } | ||||
// check params are valid values | // check params are valid values | ||||
RPCTypeCheck(request.params, {UniValue::VNUM}); | RPCTypeCheck(request.params, {UniValue::VNUM}); | ||||
int64_t delta_seconds = request.params[0].get_int64(); | int64_t delta_seconds = request.params[0].get_int64(); | ||||
if ((delta_seconds <= 0) || (delta_seconds > 3600)) { | if ((delta_seconds <= 0) || (delta_seconds > 3600)) { | ||||
throw std::runtime_error( | throw std::runtime_error( | ||||
"delta_time must be between 1 and 3600 seconds (1 hr)"); | "delta_time must be between 1 and 3600 seconds (1 hr)"); | ||||
} | } | ||||
// protect against null pointer dereference | // protect against null pointer dereference | ||||
CHECK_NONFATAL(request.context.Has<NodeContext>()); | CHECK_NONFATAL(request.context.Has<NodeContext>()); | ||||
NodeContext &node = request.context.Get<NodeContext>(); | NodeContext &node = request.context.Get<NodeContext>(); | ||||
CHECK_NONFATAL(node.scheduler); | CHECK_NONFATAL(node.scheduler); | ||||
node.scheduler->MockForward(std::chrono::seconds(delta_seconds)); | node.scheduler->MockForward(std::chrono::seconds(delta_seconds)); | ||||
return NullUniValue; | return NullUniValue; | ||||
}, | |||||
}; | |||||
} | } | ||||
static UniValue RPCLockedMemoryInfo() { | static UniValue RPCLockedMemoryInfo() { | ||||
LockedPool::Stats stats = LockedPoolManager::Instance().stats(); | LockedPool::Stats stats = LockedPoolManager::Instance().stats(); | ||||
UniValue obj(UniValue::VOBJ); | UniValue obj(UniValue::VOBJ); | ||||
obj.pushKV("used", uint64_t(stats.used)); | obj.pushKV("used", uint64_t(stats.used)); | ||||
obj.pushKV("free", uint64_t(stats.free)); | obj.pushKV("free", uint64_t(stats.free)); | ||||
obj.pushKV("total", uint64_t(stats.total)); | obj.pushKV("total", uint64_t(stats.total)); | ||||
Show All 16 Lines | if (f) { | ||||
free(ptr); | free(ptr); | ||||
return rv; | return rv; | ||||
} | } | ||||
} | } | ||||
return ""; | return ""; | ||||
} | } | ||||
#endif | #endif | ||||
static UniValue getmemoryinfo(const Config &config, | static RPCHelpMan getmemoryinfo() { | ||||
const JSONRPCRequest &request) { | |||||
/* Please, avoid using the word "pool" here in the RPC interface or help, | /* Please, avoid using the word "pool" here in the RPC interface or help, | ||||
* as users will undoubtedly confuse it with the other "memory pool" | * as users will undoubtedly confuse it with the other "memory pool" | ||||
*/ | */ | ||||
RPCHelpMan{ | return RPCHelpMan{ | ||||
"getmemoryinfo", | "getmemoryinfo", | ||||
"Returns an object containing information about memory usage.\n", | "Returns an object containing information about memory usage.\n", | ||||
{ | { | ||||
{"mode", RPCArg::Type::STR, /* default */ "\"stats\"", | {"mode", RPCArg::Type::STR, /* default */ "\"stats\"", | ||||
"determines what kind of information is returned.\n" | "determines what kind of information is returned.\n" | ||||
" - \"stats\" returns general statistics about memory usage in " | " - \"stats\" returns general statistics about memory usage in " | ||||
"the daemon.\n" | "the daemon.\n" | ||||
" - \"mallocinfo\" returns an XML string describing low-level " | " - \"mallocinfo\" returns an XML string describing low-level " | ||||
Show All 26 Lines | return RPCHelpMan{ | ||||
"Number unused chunks"}, | "Number unused chunks"}, | ||||
}}, | }}, | ||||
}}, | }}, | ||||
RPCResult{"mode \"mallocinfo\"", RPCResult::Type::STR, "", | RPCResult{"mode \"mallocinfo\"", RPCResult::Type::STR, "", | ||||
"\"<malloc version=\"1\">...\""}, | "\"<malloc version=\"1\">...\""}, | ||||
}, | }, | ||||
RPCExamples{HelpExampleCli("getmemoryinfo", "") + | RPCExamples{HelpExampleCli("getmemoryinfo", "") + | ||||
HelpExampleRpc("getmemoryinfo", "")}, | HelpExampleRpc("getmemoryinfo", "")}, | ||||
} | [&](const RPCHelpMan &self, Config &config, | ||||
.Check(request); | const JSONRPCRequest &request) -> UniValue { | ||||
std::string mode = request.params[0].isNull() | |||||
std::string mode = | ? "stats" | ||||
request.params[0].isNull() ? "stats" : request.params[0].get_str(); | : request.params[0].get_str(); | ||||
if (mode == "stats") { | if (mode == "stats") { | ||||
UniValue obj(UniValue::VOBJ); | UniValue obj(UniValue::VOBJ); | ||||
obj.pushKV("locked", RPCLockedMemoryInfo()); | obj.pushKV("locked", RPCLockedMemoryInfo()); | ||||
return obj; | return obj; | ||||
} else if (mode == "mallocinfo") { | } else if (mode == "mallocinfo") { | ||||
#ifdef HAVE_MALLOC_INFO | #ifdef HAVE_MALLOC_INFO | ||||
return RPCMallocInfo(); | return RPCMallocInfo(); | ||||
#else | #else | ||||
throw JSONRPCError( | throw JSONRPCError(RPC_INVALID_PARAMETER, | ||||
RPC_INVALID_PARAMETER, | "mallocinfo is only available when compiled " | ||||
"mallocinfo is only available when compiled with glibc 2.10+"); | "with glibc 2.10+"); | ||||
#endif | #endif | ||||
} else { | } else { | ||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "unknown mode " + mode); | throw JSONRPCError(RPC_INVALID_PARAMETER, | ||||
"unknown mode " + mode); | |||||
} | } | ||||
}, | |||||
}; | |||||
} | } | ||||
static void EnableOrDisableLogCategories(UniValue cats, bool enable) { | static void EnableOrDisableLogCategories(UniValue cats, bool enable) { | ||||
cats = cats.get_array(); | cats = cats.get_array(); | ||||
for (size_t i = 0; i < cats.size(); ++i) { | for (size_t i = 0; i < cats.size(); ++i) { | ||||
std::string cat = cats[i].get_str(); | std::string cat = cats[i].get_str(); | ||||
bool success; | bool success; | ||||
if (enable) { | if (enable) { | ||||
success = LogInstance().EnableCategory(cat); | success = LogInstance().EnableCategory(cat); | ||||
} else { | } else { | ||||
success = LogInstance().DisableCategory(cat); | success = LogInstance().DisableCategory(cat); | ||||
} | } | ||||
if (!success) { | if (!success) { | ||||
throw JSONRPCError(RPC_INVALID_PARAMETER, | throw JSONRPCError(RPC_INVALID_PARAMETER, | ||||
"unknown logging category " + cat); | "unknown logging category " + cat); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
static UniValue logging(const Config &config, const JSONRPCRequest &request) { | static RPCHelpMan logging() { | ||||
RPCHelpMan{ | return RPCHelpMan{ | ||||
"logging", | "logging", | ||||
"Gets and sets the logging configuration.\n" | "Gets and sets the logging configuration.\n" | ||||
"When called without an argument, returns the list of categories with " | "When called without an argument, returns the list of categories with " | ||||
"status that are currently being debug logged or not.\n" | "status that are currently being debug logged or not.\n" | ||||
"When called with arguments, adds or removes categories from debug " | "When called with arguments, adds or removes categories from debug " | ||||
"logging and return the lists above.\n" | "logging and return the lists above.\n" | ||||
"The arguments are evaluated in order \"include\", \"exclude\".\n" | "The arguments are evaluated in order \"include\", \"exclude\".\n" | ||||
"If an item is both included and excluded, it will thus end up being " | "If an item is both included and excluded, it will thus end up being " | ||||
Show All 30 Lines | return RPCHelpMan{ | ||||
"keys are the logging categories, and values indicates its status", | "keys are the logging categories, and values indicates its status", | ||||
{ | { | ||||
{RPCResult::Type::BOOL, "category", | {RPCResult::Type::BOOL, "category", | ||||
"if being debug logged or not. false:inactive, true:active"}, | "if being debug logged or not. false:inactive, true:active"}, | ||||
}}, | }}, | ||||
RPCExamples{ | RPCExamples{ | ||||
HelpExampleCli("logging", "\"[\\\"all\\\"]\" \"[\\\"http\\\"]\"") + | HelpExampleCli("logging", "\"[\\\"all\\\"]\" \"[\\\"http\\\"]\"") + | ||||
HelpExampleRpc("logging", "[\"all\"], [\"libevent\"]")}, | HelpExampleRpc("logging", "[\"all\"], [\"libevent\"]")}, | ||||
} | [&](const RPCHelpMan &self, Config &config, | ||||
.Check(request); | const JSONRPCRequest &request) -> UniValue { | ||||
uint32_t original_log_categories = LogInstance().GetCategoryMask(); | uint32_t original_log_categories = LogInstance().GetCategoryMask(); | ||||
if (request.params[0].isArray()) { | if (request.params[0].isArray()) { | ||||
EnableOrDisableLogCategories(request.params[0], true); | EnableOrDisableLogCategories(request.params[0], true); | ||||
} | } | ||||
if (request.params[1].isArray()) { | if (request.params[1].isArray()) { | ||||
EnableOrDisableLogCategories(request.params[1], false); | EnableOrDisableLogCategories(request.params[1], false); | ||||
} | } | ||||
uint32_t updated_log_categories = LogInstance().GetCategoryMask(); | uint32_t updated_log_categories = LogInstance().GetCategoryMask(); | ||||
uint32_t changed_log_categories = | uint32_t changed_log_categories = | ||||
original_log_categories ^ updated_log_categories; | original_log_categories ^ updated_log_categories; | ||||
/** | /** | ||||
* Update libevent logging if BCLog::LIBEVENT has changed. | * Update libevent logging if BCLog::LIBEVENT has changed. | ||||
* If the library version doesn't allow it, UpdateHTTPServerLogging() | * If the library version doesn't allow it, | ||||
* returns false, in which case we should clear the BCLog::LIBEVENT flag. | * UpdateHTTPServerLogging() returns false, in which case we should | ||||
* Throw an error if the user has explicitly asked to change only the | * clear the BCLog::LIBEVENT flag. Throw an error if the user has | ||||
* libevent flag and it failed. | * explicitly asked to change only the libevent flag and it failed. | ||||
*/ | */ | ||||
if (changed_log_categories & BCLog::LIBEVENT) { | if (changed_log_categories & BCLog::LIBEVENT) { | ||||
if (!UpdateHTTPServerLogging( | if (!UpdateHTTPServerLogging( | ||||
LogInstance().WillLogCategory(BCLog::LIBEVENT))) { | LogInstance().WillLogCategory(BCLog::LIBEVENT))) { | ||||
LogInstance().DisableCategory(BCLog::LIBEVENT); | LogInstance().DisableCategory(BCLog::LIBEVENT); | ||||
if (changed_log_categories == BCLog::LIBEVENT) { | if (changed_log_categories == BCLog::LIBEVENT) { | ||||
throw JSONRPCError(RPC_INVALID_PARAMETER, | throw JSONRPCError( | ||||
RPC_INVALID_PARAMETER, | |||||
"libevent logging cannot be updated when " | "libevent logging cannot be updated when " | ||||
"using libevent before v2.1.1."); | "using libevent before v2.1.1."); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
UniValue result(UniValue::VOBJ); | UniValue result(UniValue::VOBJ); | ||||
for (const auto &logCatActive : LogInstance().LogCategoriesList()) { | for (const auto &logCatActive : LogInstance().LogCategoriesList()) { | ||||
result.pushKV(logCatActive.category, logCatActive.active); | result.pushKV(logCatActive.category, logCatActive.active); | ||||
} | } | ||||
return result; | return result; | ||||
}, | |||||
}; | |||||
} | } | ||||
static UniValue echo(const Config &config, const JSONRPCRequest &request) { | static RPCHelpMan echo(const std::string &name) { | ||||
if (request.fHelp) { | return RPCHelpMan{ | ||||
throw std::runtime_error(RPCHelpMan{ | name, | ||||
"echo|echojson ...", | |||||
"Simply echo back the input arguments. This command is for " | "Simply echo back the input arguments. This command is for " | ||||
"testing.\n" | "testing.\n" | ||||
"\nIt will return an internal bug report when " | "\nIt will return an internal bug report when " | ||||
"arg9='trigger_internal_bug' is passed.\n" | "arg9='trigger_internal_bug' is passed.\n" | ||||
"\nThe difference between echo and echojson is that echojson has " | "\nThe difference between echo and echojson is that echojson has " | ||||
"argument conversion enabled in the client-side table in " | "argument conversion enabled in the client-side table in " | ||||
"bitcoin-cli and the GUI. There is no server-side difference.", | "bitcoin-cli and the GUI. There is no server-side difference.", | ||||
{ | { | ||||
{"arg0", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, | {"arg0", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, | ||||
""}, | ""}, | ||||
{"arg1", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, | {"arg1", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, | ||||
""}, | ""}, | ||||
{"arg2", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, | {"arg2", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, | ||||
""}, | ""}, | ||||
{"arg3", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, | {"arg3", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, | ||||
""}, | ""}, | ||||
{"arg4", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, | {"arg4", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, | ||||
""}, | ""}, | ||||
{"arg5", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, | {"arg5", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, | ||||
""}, | ""}, | ||||
{"arg6", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, | {"arg6", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, | ||||
""}, | ""}, | ||||
{"arg7", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, | {"arg7", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, | ||||
""}, | ""}, | ||||
{"arg8", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, | {"arg8", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, | ||||
""}, | ""}, | ||||
{"arg9", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, | {"arg9", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, | ||||
""}, | ""}, | ||||
}, | }, | ||||
RPCResult{RPCResult::Type::NONE, "", | RPCResult{RPCResult::Type::NONE, "", "Returns whatever was passed in"}, | ||||
"Returns whatever was passed in"}, | |||||
RPCExamples{""}, | RPCExamples{""}, | ||||
} | [&](const RPCHelpMan &self, Config &config, | ||||
.ToString()); | const JSONRPCRequest &request) -> UniValue { | ||||
if (request.fHelp) { | |||||
throw std::runtime_error(self.ToString()); | |||||
} | } | ||||
if (request.params[9].isStr()) { | if (request.params[9].isStr()) { | ||||
CHECK_NONFATAL(request.params[9].get_str() != "trigger_internal_bug"); | CHECK_NONFATAL(request.params[9].get_str() != | ||||
"trigger_internal_bug"); | |||||
} | } | ||||
return request.params; | return request.params; | ||||
}, | |||||
}; | |||||
} | |||||
static RPCHelpMan echo() { | |||||
return echo("echo"); | |||||
} | |||||
static RPCHelpMan echojson() { | |||||
return echo("echojson"); | |||||
} | } | ||||
static UniValue getcurrencyinfo(const Config &config, | static RPCHelpMan getcurrencyinfo() { | ||||
const JSONRPCRequest &request) { | return RPCHelpMan{ | ||||
RPCHelpMan{ | |||||
"getcurrencyinfo", | "getcurrencyinfo", | ||||
"Returns an object containing information about the currency.\n", | "Returns an object containing information about the currency.\n", | ||||
{}, | {}, | ||||
{ | { | ||||
RPCResult{ | RPCResult{ | ||||
RPCResult::Type::OBJ, | RPCResult::Type::OBJ, | ||||
"", | "", | ||||
"", | "", | ||||
{ | { | ||||
{RPCResult::Type::STR, "ticker", "Ticker symbol"}, | {RPCResult::Type::STR, "ticker", "Ticker symbol"}, | ||||
{RPCResult::Type::NUM, "satoshisperunit", | {RPCResult::Type::NUM, "satoshisperunit", | ||||
"Number of satoshis per base unit"}, | "Number of satoshis per base unit"}, | ||||
{RPCResult::Type::NUM, "decimals", | {RPCResult::Type::NUM, "decimals", | ||||
"Number of digits to the right of the decimal point."}, | "Number of digits to the right of the decimal point."}, | ||||
}}, | }}, | ||||
}, | }, | ||||
RPCExamples{HelpExampleCli("getcurrencyinfo", "") + | RPCExamples{HelpExampleCli("getcurrencyinfo", "") + | ||||
HelpExampleRpc("getcurrencyinfo", "")}, | HelpExampleRpc("getcurrencyinfo", "")}, | ||||
} | [&](const RPCHelpMan &self, Config &config, | ||||
.Check(request); | const JSONRPCRequest &request) -> UniValue { | ||||
const Currency ¤cy = Currency::get(); | const Currency ¤cy = Currency::get(); | ||||
UniValue res(UniValue::VOBJ); | UniValue res(UniValue::VOBJ); | ||||
res.pushKV("ticker", currency.ticker); | res.pushKV("ticker", currency.ticker); | ||||
res.pushKV("satoshisperunit", currency.baseunit / SATOSHI); | res.pushKV("satoshisperunit", currency.baseunit / SATOSHI); | ||||
res.pushKV("decimals", currency.decimals); | res.pushKV("decimals", currency.decimals); | ||||
return res; | return res; | ||||
}, | |||||
}; | |||||
} | } | ||||
void RegisterMiscRPCCommands(CRPCTable &t) { | void RegisterMiscRPCCommands(CRPCTable &t) { | ||||
// clang-format off | // clang-format off | ||||
static const CRPCCommand commands[] = { | static const CRPCCommand commands[] = { | ||||
// category name actor (function) argNames | // category name actor (function) argNames | ||||
// ------------------- ------------------------ ---------------------- ---------- | // ------------------- ------------------------ ---------------------- ---------- | ||||
{ "control", "getmemoryinfo", getmemoryinfo, {"mode"} }, | { "control", "getmemoryinfo", getmemoryinfo, {"mode"} }, | ||||
{ "control", "logging", logging, {"include", "exclude"} }, | { "control", "logging", logging, {"include", "exclude"} }, | ||||
{ "util", "validateaddress", validateaddress, {"address"} }, | { "util", "validateaddress", validateaddress, {"address"} }, | ||||
{ "util", "createmultisig", createmultisig, {"nrequired","keys"} }, | { "util", "createmultisig", createmultisig, {"nrequired","keys"} }, | ||||
{ "util", "deriveaddresses", deriveaddresses, {"descriptor", "range"} }, | { "util", "deriveaddresses", deriveaddresses, {"descriptor", "range"} }, | ||||
{ "util", "getdescriptorinfo", getdescriptorinfo, {"descriptor"} }, | { "util", "getdescriptorinfo", getdescriptorinfo, {"descriptor"} }, | ||||
{ "util", "verifymessage", verifymessage, {"address","signature","message"} }, | { "util", "verifymessage", verifymessage, {"address","signature","message"} }, | ||||
{ "util", "signmessagewithprivkey", signmessagewithprivkey, {"privkey","message"} }, | { "util", "signmessagewithprivkey", signmessagewithprivkey, {"privkey","message"} }, | ||||
{ "util", "getcurrencyinfo", getcurrencyinfo, {} }, | { "util", "getcurrencyinfo", getcurrencyinfo, {} }, | ||||
/* Not shown in help */ | /* Not shown in help */ | ||||
{ "hidden", "setmocktime", setmocktime, {"timestamp"}}, | { "hidden", "setmocktime", setmocktime, {"timestamp"}}, | ||||
{ "hidden", "mockscheduler", mockscheduler, {"delta_time"}}, | { "hidden", "mockscheduler", mockscheduler, {"delta_time"}}, | ||||
{ "hidden", "echo", echo, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}}, | { "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"}}, | { "hidden", "echojson", echojson, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}}, | ||||
}; | }; | ||||
// clang-format on | // clang-format on | ||||
for (const auto &c : commands) { | for (const auto &c : commands) { | ||||
t.appendCommand(c.name, &c); | t.appendCommand(c.name, &c); | ||||
} | } | ||||
} | } |