diff options
Diffstat (limited to 'src/rpc/misc.cpp')
-rw-r--r-- | src/rpc/misc.cpp | 285 |
1 files changed, 1 insertions, 284 deletions
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 7525b859ee..ceeebe8483 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -3,6 +3,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include <chainparams.h> #include <httpserver.h> #include <index/blockfilterindex.h> #include <index/coinstatsindex.h> @@ -11,303 +12,23 @@ #include <interfaces/echo.h> #include <interfaces/init.h> #include <interfaces/ipc.h> -#include <key_io.h> #include <node/context.h> -#include <outputtype.h> #include <rpc/server.h> #include <rpc/server_util.h> #include <rpc/util.h> #include <scheduler.h> -#include <script/descriptor.h> #include <univalue.h> #include <util/check.h> -#include <util/strencodings.h> #include <util/syscall_sandbox.h> #include <util/system.h> -#include <optional> #include <stdint.h> -#include <tuple> #ifdef HAVE_MALLOC_INFO #include <malloc.h> #endif using node::NodeContext; -static RPCHelpMan validateaddress() -{ - return RPCHelpMan{ - "validateaddress", - "\nReturn information about the given bitcoin address.\n", - { - {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address to validate"}, - }, - RPCResult{ - RPCResult::Type::OBJ, "", "", - { - {RPCResult::Type::BOOL, "isvalid", "If the address is valid or not"}, - {RPCResult::Type::STR, "address", /*optional=*/true, "The bitcoin address validated"}, - {RPCResult::Type::STR_HEX, "scriptPubKey", /*optional=*/true, "The hex-encoded scriptPubKey generated by the address"}, - {RPCResult::Type::BOOL, "isscript", /*optional=*/true, "If the key is a script"}, - {RPCResult::Type::BOOL, "iswitness", /*optional=*/true, "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"}, - {RPCResult::Type::STR, "error", /*optional=*/true, "Error message, if any"}, - {RPCResult::Type::ARR, "error_locations", /*optional=*/true, "Indices of likely error locations in address, if known (e.g. Bech32 errors)", - { - {RPCResult::Type::NUM, "index", "index of a potential error"}, - }}, - } - }, - RPCExamples{ - HelpExampleCli("validateaddress", "\"" + EXAMPLE_ADDRESS[0] + "\"") + - HelpExampleRpc("validateaddress", "\"" + EXAMPLE_ADDRESS[0] + "\"") - }, - [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue -{ - std::string error_msg; - std::vector<int> error_locations; - CTxDestination dest = DecodeDestination(request.params[0].get_str(), error_msg, &error_locations); - const bool isValid = IsValidDestination(dest); - CHECK_NONFATAL(isValid == error_msg.empty()); - - UniValue ret(UniValue::VOBJ); - ret.pushKV("isvalid", isValid); - if (isValid) { - std::string currentAddress = EncodeDestination(dest); - ret.pushKV("address", currentAddress); - - CScript scriptPubKey = GetScriptForDestination(dest); - ret.pushKV("scriptPubKey", HexStr(scriptPubKey)); - - UniValue detail = DescribeAddress(dest); - ret.pushKVs(detail); - } else { - UniValue error_indices(UniValue::VARR); - for (int i : error_locations) error_indices.push_back(i); - ret.pushKV("error_locations", error_indices); - ret.pushKV("error", error_msg); - } - - return ret; -}, - }; -} - -static RPCHelpMan createmultisig() -{ - return RPCHelpMan{"createmultisig", - "\nCreates a multi-signature address with n signature of m keys required.\n" - "It returns a json object with the address and redeemScript.\n", - { - {"nrequired", RPCArg::Type::NUM, RPCArg::Optional::NO, "The number of required signatures out of the n keys."}, - {"keys", RPCArg::Type::ARR, RPCArg::Optional::NO, "The hex-encoded public keys.", - { - {"key", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "The hex-encoded public key"}, - }}, - {"address_type", RPCArg::Type::STR, RPCArg::Default{"legacy"}, "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."}, - }, - RPCResult{ - 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"}, - {RPCResult::Type::ARR, "warnings", /*optional=*/true, "Any warnings resulting from the creation of this multisig", - { - {RPCResult::Type::STR, "", ""}, - }}, - } - }, - RPCExamples{ - "\nCreate a multisig address from 2 public keys\n" - + HelpExampleCli("createmultisig", "2 \"[\\\"03789ed0bb717d88f7d321a368d905e7430207ebbd82bd342cf11ae157a7ace5fd\\\",\\\"03dbc6764b8884a92e871274b87583e6d5c2a58819473e17e107ef3f6aa5a61626\\\"]\"") + - "\nAs a JSON-RPC call\n" - + HelpExampleRpc("createmultisig", "2, [\"03789ed0bb717d88f7d321a368d905e7430207ebbd82bd342cf11ae157a7ace5fd\",\"03dbc6764b8884a92e871274b87583e6d5c2a58819473e17e107ef3f6aa5a61626\"]") - }, - [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue -{ - int required = request.params[0].get_int(); - - // Get the public keys - const UniValue& keys = request.params[1].get_array(); - std::vector<CPubKey> pubkeys; - for (unsigned int i = 0; i < keys.size(); ++i) { - if (IsHex(keys[i].get_str()) && (keys[i].get_str().length() == 66 || keys[i].get_str().length() == 130)) { - pubkeys.push_back(HexToPubKey(keys[i].get_str())); - } else { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Invalid public key: %s\n.", keys[i].get_str())); - } - } - - // Get the output type - OutputType output_type = OutputType::LEGACY; - if (!request.params[2].isNull()) { - std::optional<OutputType> parsed = ParseOutputType(request.params[2].get_str()); - if (!parsed) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[2].get_str())); - } else if (parsed.value() == OutputType::BECH32M) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "createmultisig cannot create bech32m multisig addresses"); - } - output_type = parsed.value(); - } - - // Construct using pay-to-script-hash: - FillableSigningProvider keystore; - 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)); - result.pushKV("descriptor", descriptor->ToString()); - - UniValue warnings(UniValue::VARR); - if (!request.params[2].isNull() && OutputTypeFromDestination(dest) != output_type) { - // Only warns if the user has explicitly chosen an address type we cannot generate - warnings.push_back("Unable to make chosen address type, please ensure no uncompressed public keys are present."); - } - if (warnings.size()) result.pushKV("warnings", warnings); - - return result; -}, - }; -} - -static RPCHelpMan getdescriptorinfo() -{ - const std::string EXAMPLE_DESCRIPTOR = "wpkh([d34db33f/84h/0h/0h]0279be667ef9dcbbac55a06295Ce870b07029Bfcdb2dce28d959f2815b16f81798)"; - - return RPCHelpMan{"getdescriptorinfo", - {"\nAnalyses a descriptor.\n"}, - { - {"descriptor", RPCArg::Type::STR, RPCArg::Optional::NO, "The descriptor."}, - }, - RPCResult{ - 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" + - HelpExampleCli("getdescriptorinfo", "\"" + EXAMPLE_DESCRIPTOR + "\"") + - HelpExampleRpc("getdescriptorinfo", "\"" + EXAMPLE_DESCRIPTOR + "\"") - }, - [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue -{ - RPCTypeCheck(request.params, {UniValue::VSTR}); - - FlatSigningProvider provider; - std::string error; - auto desc = Parse(request.params[0].get_str(), provider, error); - if (!desc) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error); - } - - UniValue result(UniValue::VOBJ); - result.pushKV("descriptor", desc->ToString()); - result.pushKV("checksum", GetDescriptorChecksum(request.params[0].get_str())); - result.pushKV("isrange", desc->IsRange()); - result.pushKV("issolvable", desc->IsSolvable()); - result.pushKV("hasprivatekeys", provider.keys.size() > 0); - return result; -}, - }; -} - -static RPCHelpMan deriveaddresses() -{ - const std::string EXAMPLE_DESCRIPTOR = "wpkh([d34db33f/84h/0h/0h]xpub6DJ2dNUysrn5Vt36jH2KLBT2i1auw1tTSSomg8PhqNiUtx8QX2SvC9nrHu81fT41fvDUnhMjEzQgXnQjKEu3oaqMSzhSrHMxyyoEAmUHQbY/0/*)#cjjspncu"; - - return 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."}, - {"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{ - RPCResult::Type::ARR, "", "", - { - {RPCResult::Type::STR, "address", "the derived addresses"}, - } - }, - RPCExamples{ - "First three native segwit receive addresses\n" + - HelpExampleCli("deriveaddresses", "\"" + EXAMPLE_DESCRIPTOR + "\" \"[0,2]\"") + - HelpExampleRpc("deriveaddresses", "\"" + EXAMPLE_DESCRIPTOR + "\", \"[0,2]\"") - }, - [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue -{ - RPCTypeCheck(request.params, {UniValue::VSTR, UniValueType()}); // Range argument is checked later - const std::string desc_str = request.params[0].get_str(); - - int64_t range_begin = 0; - int64_t range_end = 0; - - if (request.params.size() >= 2 && !request.params[1].isNull()) { - std::tie(range_begin, range_end) = ParseDescriptorRange(request.params[1]); - } - - FlatSigningProvider key_provider; - std::string error; - auto desc = Parse(desc_str, key_provider, error, /* require_checksum = */ true); - if (!desc) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error); - } - - 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) { - FlatSigningProvider provider; - std::vector<CScript> scripts; - if (!desc->Expand(i, key_provider, scripts, provider)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Cannot derive script without private keys"); - } - - for (const CScript &script : scripts) { - CTxDestination dest; - if (!ExtractDestination(script, dest)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "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 RPCHelpMan setmocktime() { return RPCHelpMan{"setmocktime", @@ -703,10 +424,6 @@ void RegisterMiscRPCCommands(CRPCTable& t) static const CRPCCommand commands[]{ {"control", &getmemoryinfo}, {"control", &logging}, - {"util", &validateaddress}, - {"util", &createmultisig}, - {"util", &deriveaddresses}, - {"util", &getdescriptorinfo}, {"util", &getindexinfo}, {"hidden", &setmocktime}, {"hidden", &mockscheduler}, |