LCOV - code coverage report
Current view: top level - src/wallet/rpc - addresses.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 442 458 96.5 %
Date: 2026-06-25 07:23:43 Functions: 32 33 97.0 %

          Line data    Source code
       1             : // Copyright (c) 2011-2021 The Bitcoin Core developers
       2             : // Distributed under the MIT software license, see the accompanying
       3             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       4             : 
       5             : #include <core_io.h>
       6             : #include <key_io.h>
       7             : #include <rpc/util.h>
       8             : #include <util/bip32.h>
       9             : #include <util/translation.h>
      10             : #include <wallet/receive.h>
      11             : #include <wallet/rpc/util.h>
      12             : #include <wallet/wallet.h>
      13             : 
      14             : #include <univalue.h>
      15             : 
      16             : namespace wallet {
      17       26352 : RPCHelpMan getnewaddress()
      18             : {
      19       52704 :     return RPCHelpMan{"getnewaddress",
      20       26352 :                 "\nReturns a new Dash address for receiving payments.\n"
      21             :                 "If 'label' is specified, it is added to the address book \n"
      22             :                 "so payments received with the address will be associated with 'label'.\n",
      23       52704 :                 {
      24       26352 :                     {"label", RPCArg::Type::STR, RPCArg::Default{""}, "The label name for the address to be linked to. It can also be set to the empty string \"\" to represent the default label. The label does not need to exist, it will be created if there is no label by the given name."},
      25             :                 },
      26       26352 :                 RPCResult{
      27       26352 :                     RPCResult::Type::STR, "address", "The new Dash address"
      28             :                 },
      29       26352 :                 RPCExamples{
      30       26352 :                     HelpExampleCli("getnewaddress", "")
      31       26352 :             + HelpExampleRpc("getnewaddress", "")
      32             :                 },
      33       49772 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
      34             : {
      35       23420 :     std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
      36       23420 :     if (!pwallet) return UniValue::VNULL;
      37             : 
      38       23420 :     LOCK(pwallet->cs_wallet);
      39             : 
      40       23420 :     if (!pwallet->CanGetAddresses()) {
      41          62 :         throw JSONRPCError(RPC_WALLET_ERROR, "Error: This wallet has no available keys");
      42             :     }
      43             : 
      44             :     // Parse the label first so we don't generate a key if there's an error
      45       23358 :     std::string label;
      46       23358 :     if (!request.params[0].isNull())
      47         298 :         label = LabelFromValue(request.params[0]);
      48             : 
      49       23358 :     auto op_dest = pwallet->GetNewDestination(label);
      50       23358 :     if (!op_dest) {
      51          16 :         throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, util::ErrorString(op_dest).original);
      52             :     }
      53       23342 :     return EncodeDestination(*op_dest);
      54       23498 : },
      55             :     };
      56           0 : }
      57             : 
      58        3225 : RPCHelpMan getrawchangeaddress()
      59             : {
      60        6450 :     return RPCHelpMan{"getrawchangeaddress",
      61        3225 :                 "\nReturns a new Dash address, for receiving change.\n"
      62             :                 "This is for use with raw transactions, NOT normal use.\n",
      63        3225 :                 {},
      64        3225 :                 RPCResult{
      65        3225 :                     RPCResult::Type::STR, "address", "The address"
      66             :                 },
      67        3225 :                 RPCExamples{
      68        3225 :                     HelpExampleCli("getrawchangeaddress", "")
      69        3225 :             + HelpExampleRpc("getrawchangeaddress", "")
      70             :                 },
      71        3554 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
      72             : {
      73         329 :     std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
      74         329 :     if (!pwallet) return UniValue::VNULL;
      75             : 
      76         329 :     LOCK(pwallet->cs_wallet);
      77             : 
      78         329 :     if (!pwallet->CanGetAddresses(true)) {
      79          66 :         throw JSONRPCError(RPC_WALLET_ERROR, "Error: This wallet has no available keys");
      80             :     }
      81             : 
      82         263 :     auto op_dest = pwallet->GetNewChangeDestination();
      83         263 :     if (!op_dest) {
      84           6 :         throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, util::ErrorString(op_dest).original);
      85             :     }
      86         257 :     return EncodeDestination(*op_dest);
      87         401 : },
      88             :     };
      89           0 : }
      90             : 
      91        2993 : RPCHelpMan setlabel()
      92             : {
      93        5986 :     return RPCHelpMan{"setlabel",
      94        2993 :                 "\nSets the label associated with the given address.\n",
      95        8979 :                 {
      96        2993 :                     {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The Dash address to be associated with a label."},
      97        2993 :                     {"label", RPCArg::Type::STR, RPCArg::Optional::NO, "The label to assign to the address."},
      98             :                 },
      99        2993 :                 RPCResult{RPCResult::Type::NONE, "", ""},
     100        2993 :                 RPCExamples{
     101        2993 :                     HelpExampleCli("setlabel", "\"" + EXAMPLE_ADDRESS[0] + "\" \"tabby\"")
     102        2993 :             + HelpExampleRpc("setlabel", "\"" + EXAMPLE_ADDRESS[0] + "\", \"tabby\"")
     103             :                 },
     104        3090 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     105             : {
     106          97 :     std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
     107          97 :     if (!pwallet) return UniValue::VNULL;
     108             : 
     109          97 :     LOCK(pwallet->cs_wallet);
     110             : 
     111          97 :     CTxDestination dest = DecodeDestination(request.params[0].get_str());
     112          97 :     if (!IsValidDestination(dest)) {
     113           3 :         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Dash address");
     114             :     }
     115             : 
     116          94 :     std::string label = LabelFromValue(request.params[1]);
     117             : 
     118          94 :     if (pwallet->IsMine(dest)) {
     119          86 :         pwallet->SetAddressBook(dest, label, "receive");
     120          86 :     } else {
     121           8 :         pwallet->SetAddressBook(dest, label, "send");
     122             :     }
     123             : 
     124          94 :     return UniValue::VNULL;
     125         100 : },
     126             :     };
     127           0 : }
     128             : 
     129        2912 : RPCHelpMan listaddressgroupings()
     130             : {
     131        5824 :     return RPCHelpMan{"listaddressgroupings",
     132        2912 :                 "\nLists groups of addresses which have had their common ownership\n"
     133             :                 "made public by common use as inputs or as the resulting change\n"
     134             :                 "in past transactions\n",
     135        2912 :                 {},
     136        2912 :                 RPCResult{
     137        2912 :                     RPCResult::Type::ARR, "", "",
     138        5824 :                     {
     139        5824 :                         {RPCResult::Type::ARR, "", "",
     140        5824 :                         {
     141        5824 :                             {RPCResult::Type::ARR_FIXED, "", "",
     142       11648 :                             {
     143        2912 :                                 {RPCResult::Type::STR, "address", "The Dash address"},
     144        2912 :                                 {RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT},
     145        2912 :                                 {RPCResult::Type::STR, "label", /*optional=*/true, "The label"},
     146             :                             }},
     147             :                         }},
     148             :                     }
     149             :                 },
     150        2912 :                 RPCExamples{
     151        2912 :                     HelpExampleCli("listaddressgroupings", "")
     152        2912 :             + HelpExampleRpc("listaddressgroupings", "")
     153             :                 },
     154        2928 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     155             : {
     156          16 :     const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
     157          16 :     if (!pwallet) return UniValue::VNULL;
     158             : 
     159             :     // Make sure the results are valid at least up to the most recent block
     160             :     // the user could have gotten from another RPC command prior to now
     161          16 :     pwallet->BlockUntilSyncedToCurrentChain();
     162             : 
     163          16 :     LOCK(pwallet->cs_wallet);
     164             : 
     165          16 :     UniValue jsonGroupings(UniValue::VARR);
     166          16 :     std::map<CTxDestination, CAmount> balances = GetAddressBalances(*pwallet);
     167          74 :     for (const std::set<CTxDestination>& grouping : GetAddressGroupings(*pwallet)) {
     168          58 :         UniValue jsonGrouping(UniValue::VARR);
     169         142 :         for (const CTxDestination& address : grouping)
     170             :         {
     171          84 :             UniValue addressInfo(UniValue::VARR);
     172          84 :             addressInfo.push_back(EncodeDestination(address));
     173          84 :             addressInfo.push_back(ValueFromAmount(balances[address]));
     174             :             {
     175          84 :                 const auto* address_book_entry = pwallet->FindAddressBookEntry(address);
     176          84 :                 if (address_book_entry) {
     177          64 :                     addressInfo.push_back(address_book_entry->GetLabel());
     178          64 :                 }
     179             :             }
     180          84 :             jsonGrouping.push_back(addressInfo);
     181          84 :         }
     182          58 :         jsonGroupings.push_back(jsonGrouping);
     183          58 :     }
     184          16 :     return jsonGroupings;
     185          16 : },
     186             :     };
     187           0 : }
     188             : 
     189        2963 : RPCHelpMan addmultisigaddress()
     190             : {
     191        5926 :     return RPCHelpMan{"addmultisigaddress",
     192        2963 :                 "\nAdd an nrequired-to-sign multisignature address to the wallet. Requires a new wallet backup.\n"
     193             :                 "Each key is a Dash address or hex-encoded public key.\n"
     194             :                 "This functionality is only intended for use with non-watchonly addresses.\n"
     195             :                 "See `importaddress` for watchonly p2sh address support.\n"
     196             :                 "If 'label' is specified, assign address to that label.\n"
     197             :                 "Note: This command is only compatible with legacy wallets.\n",
     198       11852 :                 {
     199        2963 :                     {"nrequired", RPCArg::Type::NUM, RPCArg::Optional::NO, "The number of required signatures out of the n keys or addresses."},
     200        5926 :                     {"keys", RPCArg::Type::ARR, RPCArg::Optional::NO, "The Dash addresses or hex-encoded public keys",
     201        5926 :                         {
     202        2963 :                             {"key", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Dash address or hex-encoded public key"},
     203             :                         },
     204             :                         },
     205        2963 :                     {"label", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "A label to assign the addresses to."},
     206             :                 },
     207        2963 :                 RPCResult{
     208        2963 :                     RPCResult::Type::OBJ, "", "",
     209       11852 :                     {
     210        2963 :                         {RPCResult::Type::STR, "address", "The value of the new multisig address"},
     211        2963 :                         {RPCResult::Type::STR_HEX, "redeemScript", "The string value of the hex-encoded redemption script"},
     212        2963 :                         {RPCResult::Type::STR, "descriptor", "The descriptor for this multisig."},
     213             :                     }
     214             :                 },
     215        2963 :                 RPCExamples{
     216             :             "\nAdd a multisig address from 2 addresses\n"
     217        2963 :             + HelpExampleCli("addmultisigaddress", "2 \"[\\\"" + EXAMPLE_ADDRESS[0] + "\\\",\\\"" + EXAMPLE_ADDRESS[1] + "\\\"]\"") +
     218             :             "\nAs a JSON-RPC call\n"
     219        2963 :             + HelpExampleRpc("addmultisigaddress", "2, \"[\\\"" + EXAMPLE_ADDRESS[0] + "\\\",\\\"" + EXAMPLE_ADDRESS[1] + "\\\"]\"")
     220             :                 },
     221        3030 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     222             : {
     223          67 :     std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
     224          67 :     if (!pwallet) return UniValue::VNULL;
     225             : 
     226          67 :     LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet);
     227             : 
     228          65 :     LOCK2(pwallet->cs_wallet, spk_man.cs_KeyStore);
     229             : 
     230          65 :     std::string label;
     231          65 :     if (!request.params[2].isNull())
     232          27 :         label = LabelFromValue(request.params[2]);
     233             : 
     234          65 :     int required = request.params[0].getInt<int>();
     235             : 
     236             :     // Get the public keys
     237          65 :     const UniValue& keys_or_addrs = request.params[1].get_array();
     238          65 :     std::vector<CPubKey> pubkeys;
     239         342 :     for (unsigned int i = 0; i < keys_or_addrs.size(); ++i) {
     240         281 :         if (IsHex(keys_or_addrs[i].get_str()) && (keys_or_addrs[i].get_str().length() == 66 || keys_or_addrs[i].get_str().length() == 130)) {
     241         110 :             pubkeys.push_back(HexToPubKey(keys_or_addrs[i].get_str()));
     242         110 :         } else {
     243         171 :             pubkeys.push_back(AddrToPubKey(spk_man, keys_or_addrs[i].get_str()));
     244             :         }
     245         277 :     }
     246             : 
     247             :     // Construct using pay-to-script-hash:
     248          61 :     CScript inner;
     249          61 :     CTxDestination dest = AddAndGetMultisigDestination(required, pubkeys, spk_man, inner);
     250          61 :     pwallet->SetAddressBook(dest, label, "send");
     251             : 
     252             :     // Make the descriptor
     253          61 :     std::unique_ptr<Descriptor> descriptor = InferDescriptor(GetScriptForDestination(dest), spk_man);
     254             : 
     255          61 :     UniValue result(UniValue::VOBJ);
     256          61 :     result.pushKV("address", EncodeDestination(dest));
     257          61 :     result.pushKV("redeemScript", HexStr(inner));
     258          61 :     result.pushKV("descriptor", descriptor->ToString());
     259          61 :     return result;
     260          67 : },
     261             :     };
     262           0 : }
     263             : 
     264        2966 : RPCHelpMan keypoolrefill()
     265             : {
     266        5932 :     return RPCHelpMan{"keypoolrefill",
     267        2966 :         "\nFills the keypool."+
     268             :                 HELP_REQUIRING_PASSPHRASE,
     269        5932 :         {
     270        2966 :             {"newsize", RPCArg::Type::NUM, RPCArg::DefaultHint{strprintf("%u, or as set by -keypool", DEFAULT_KEYPOOL_SIZE)}, "The new keypool size"},
     271             :         },
     272        2966 :         RPCResult{RPCResult::Type::NONE, "", ""},
     273        2966 :         RPCExamples{
     274        2966 :             HelpExampleCli("keypoolrefill", "")
     275        2966 :     + HelpExampleRpc("keypoolrefill", "")
     276             :         },
     277        3036 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     278             : {
     279          70 :     std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
     280          70 :     if (!pwallet) return UniValue::VNULL;
     281             : 
     282          70 :     if (pwallet->IsLegacy() && pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
     283           2 :         throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet");
     284             :     }
     285             : 
     286          68 :     LOCK(pwallet->cs_wallet);
     287             : 
     288             :     // 0 is interpreted by TopUpKeyPool() as the default keypool size given by -keypool
     289          68 :     unsigned int kpSize = 0;
     290          68 :     if (!request.params[0].isNull()) {
     291          60 :         if (request.params[0].getInt<int>() < 0)
     292           0 :             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size.");
     293          60 :         kpSize = (unsigned int)request.params[0].getInt<int>();
     294          60 :     }
     295             : 
     296          68 :     EnsureWalletIsUnlocked(*pwallet);
     297          68 :     pwallet->TopUpKeyPool(kpSize);
     298             : 
     299          68 :     if (pwallet->GetKeyPoolSize() < (pwallet->IsHDEnabled() ? kpSize * 2 : kpSize)) {
     300           0 :         throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool.");
     301             :     }
     302             : 
     303          68 :     return UniValue::VNULL;
     304          72 : },
     305             :     };
     306           0 : }
     307             : 
     308        2898 : RPCHelpMan newkeypool()
     309             : {
     310        5796 :     return RPCHelpMan{"newkeypool",
     311             :                 "\nEntirely clears and refills the keypool.\n"
     312             :                 "WARNING: On non-HD wallets, this will require a new backup immediately, to include the new keys.\n"
     313             :                 "When restoring a backup of an HD wallet created before the newkeypool command is run, funds received to\n"
     314             :                 "new addresses may not appear automatically. They have not been lost, but the wallet may not find them.\n"
     315             :                 "This can be fixed by running the newkeypool command on the backup and then rescanning, so the wallet\n"
     316             :                 "re-generates the required keys.\n"
     317        2898 :                 "Note: This command is only compatible with legacy wallets.\n" +
     318             :             HELP_REQUIRING_PASSPHRASE,
     319        2898 :                 {},
     320        2898 :                 RPCResult{RPCResult::Type::NONE, "", ""},
     321        2898 :                 RPCExamples{
     322        2898 :             HelpExampleCli("newkeypool", "")
     323        2898 :             + HelpExampleRpc("newkeypool", "")
     324             :                 },
     325        2900 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     326             : {
     327           2 :     std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
     328           2 :     if (!pwallet) return UniValue::VNULL;
     329             : 
     330           2 :     LOCK(pwallet->cs_wallet);
     331             : 
     332           2 :     LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet, true);
     333           2 :     spk_man.NewKeyPool();
     334             : 
     335           2 :     return UniValue::VNULL;
     336           2 : },
     337             :     };
     338           0 : }
     339             : 
     340             : class DescribeWalletAddressVisitor
     341             : {
     342             : public:
     343             :     const SigningProvider * const provider;
     344             : 
     345          86 :     void ProcessSubScript(const CScript& subscript, UniValue& obj) const
     346             :     {
     347             :         // Always present: script type and redeemscript
     348          86 :         std::vector<std::vector<unsigned char>> solutions_data;
     349          86 :         TxoutType whichType = Solver(subscript, solutions_data);
     350          86 :         obj.pushKV("script", GetTxnOutputType(whichType));
     351          86 :         obj.pushKV("hex", HexStr(subscript));
     352             : 
     353          86 :         CTxDestination embedded;
     354          86 :         if (ExtractDestination(subscript, embedded)) {
     355             :             // Only when the script corresponds to an address.
     356          17 :             UniValue subobj(UniValue::VOBJ);
     357          17 :             UniValue detail = DescribeAddress(embedded);
     358          17 :             subobj.pushKVs(detail);
     359          17 :             UniValue wallet_detail = std::visit(*this, embedded);
     360          17 :             subobj.pushKVs(wallet_detail);
     361          17 :             subobj.pushKV("address", EncodeDestination(embedded));
     362          17 :             subobj.pushKV("scriptPubKey", HexStr(subscript));
     363             :             // Always report the pubkey at the top level, so that `getnewaddress()['pubkey']` always works.
     364          17 :             if (subobj.exists("pubkey")) obj.pushKV("pubkey", subobj["pubkey"]);
     365          17 :             obj.pushKV("embedded", std::move(subobj));
     366          86 :         } else if (whichType == TxoutType::MULTISIG) {
     367             :             // Also report some information on multisig scripts (which do not have a corresponding address).
     368          69 :             UniValue pubkeys(UniValue::VARR);
     369          69 :             UniValue addresses(UniValue::VARR);
     370         456 :             for (size_t i = 1; i < solutions_data.size() - 1; ++i) {
     371         387 :                 CPubKey pubkey(solutions_data[i]);
     372         387 :                 pubkeys.push_back(HexStr(pubkey));
     373         387 :                 addresses.push_back(EncodeDestination(PKHash(pubkey)));
     374         387 :             }
     375          69 :             obj.pushKV("pubkeys", std::move(pubkeys));
     376          69 :             obj.pushKV("addresses", std::move(addresses));
     377          69 :             obj.pushKV("sigsrequired", solutions_data[0][0]);
     378          69 :         }
     379          86 :     }
     380             : 
     381        3888 :     explicit DescribeWalletAddressVisitor(const SigningProvider * const _provider) : provider(_provider) {}
     382             : 
     383           0 :     UniValue operator()(const CNoDestination &dest) const { return UniValue(UniValue::VOBJ); }
     384             : 
     385        1856 :     UniValue operator()(const PKHash& pkhash) const {
     386        1856 :         CKeyID keyID{ToKeyID(pkhash)};
     387        1856 :         UniValue obj(UniValue::VOBJ);
     388        1856 :         CPubKey vchPubKey;
     389        1856 :         if (provider && provider->GetPubKey(keyID, vchPubKey)) {
     390        1589 :             obj.pushKV("pubkey", HexStr(vchPubKey));
     391        1589 :             obj.pushKV("iscompressed", vchPubKey.IsCompressed());
     392        1589 :         }
     393        1856 :         return obj;
     394        1856 :     }
     395             : 
     396         105 :     UniValue operator()(const ScriptHash& scriptHash) const {
     397         105 :         CScriptID scriptID(scriptHash);
     398         105 :         UniValue obj(UniValue::VOBJ);
     399         105 :         CScript subscript;
     400         105 :         if (provider && provider->GetCScript(scriptID, subscript)) {
     401          86 :             ProcessSubScript(subscript, obj);
     402          86 :         }
     403         105 :         return obj;
     404         105 :     }
     405             : };
     406             : 
     407        1944 : static UniValue DescribeWalletAddress(const CWallet& wallet, const CTxDestination& dest)
     408             : {
     409        1944 :     UniValue ret(UniValue::VOBJ);
     410        1944 :     UniValue detail = DescribeAddress(dest);
     411        1944 :     CScript script = GetScriptForDestination(dest);
     412        1944 :     std::unique_ptr<SigningProvider> provider = nullptr;
     413        1944 :     provider = wallet.GetSolvingProvider(script);
     414        1944 :     ret.pushKVs(detail);
     415        1944 :     ret.pushKVs(std::visit(DescribeWalletAddressVisitor(provider.get()), dest));
     416        1944 :     return ret;
     417        1944 : }
     418             : 
     419        4850 : RPCHelpMan getaddressinfo()
     420             : {
     421        9700 :     return RPCHelpMan{"getaddressinfo",
     422        4850 :                 "\nReturn information about the given Dash address.\n"
     423             :                 "Some of the information will only be present if the address is in the active wallet.\n",
     424        9700 :                 {
     425        4850 :                     {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The Dash address for which to get information."},
     426             :                 },
     427        4850 :                 RPCResult{
     428        4850 :                     RPCResult::Type::OBJ, "", "",
     429      116400 :                     {
     430        4850 :                         {RPCResult::Type::STR, "address", "The Dash address validated."},
     431        4850 :                         {RPCResult::Type::STR_HEX, "scriptPubKey", "The hex-encoded scriptPubKey generated by the address."},
     432        4850 :                         {RPCResult::Type::BOOL, "ismine", "If the address is yours."},
     433        4850 :                         {RPCResult::Type::BOOL, "iswatchonly", "If the address is watchonly."},
     434        4850 :                         {RPCResult::Type::BOOL, "solvable", "If we know how to spend coins sent to this address, ignoring the possible lack of private keys."},
     435        4850 :                         {RPCResult::Type::STR, "desc", /*optional=*/true, "A descriptor for spending coins sent to this address (only when solvable)."},
     436        4850 :                         {RPCResult::Type::STR, "parent_desc", /*optional=*/true, "The descriptor used to derive this address if this is a descriptor wallet"},
     437        4850 :                         {RPCResult::Type::BOOL, "isscript", "If the key is a script."},
     438        4850 :                         {RPCResult::Type::BOOL, "ischange", "If the address was used for change output."},
     439        4850 :                         {RPCResult::Type::STR, "script", /*optional=*/true, "The output script type. Only if isscript is true and the redeemscript is known. Possible types: nonstandard, pubkey, pubkeyhash, scripthash, multisig, nulldata"},
     440        4850 :                         {RPCResult::Type::STR_HEX, "hex", /*optional=*/true, "The redeemscript for the p2sh address."},
     441        9700 :                         {RPCResult::Type::ARR, "pubkeys", /*optional=*/true, "Array of pubkeys associated with the known redeemscript (only if script is multisig).",
     442        9700 :                         {
     443        4850 :                             {RPCResult::Type::STR, "pubkey", ""},
     444             :                         }},
     445        9700 :                         {RPCResult::Type::ARR, "addresses", /*optional=*/true, "Array of addresses associated with the known redeemscript (only if script is multisig).",
     446        9700 :                         {
     447        4850 :                             {RPCResult::Type::STR, "address", ""},
     448             :                         }},
     449        4850 :                         {RPCResult::Type::NUM, "sigsrequired", /*optional=*/true, "The number of signatures required to spend multisig output (only if script is multisig)."},
     450        4850 :                         {RPCResult::Type::STR_HEX, "pubkey", /*optional=*/true, "The hex value of the raw public key, for single-key addresses."},
     451        9700 :                         {RPCResult::Type::OBJ, "embedded", /*optional=*/true, "Information about the address embedded in P2SH, if relevant and known.",
     452        9700 :                         {
     453        4850 :                             {RPCResult::Type::ELISION, "", "Includes all getaddressinfo output fields for the embedded address, excluding metadata (timestamp, hdkeypath, hdseedid)\n"
     454             :                             "and relation to the wallet (ismine, iswatchonly)."},
     455             :                         }},
     456        4850 :                         {RPCResult::Type::BOOL, "iscompressed", /*optional=*/true, "If the pubkey is compressed."},
     457        4850 :                         {RPCResult::Type::NUM_TIME, "timestamp", /*optional=*/true, "The creation time of the key, if available, expressed in " + UNIX_EPOCH_TIME + "."},
     458        4850 :                         {RPCResult::Type::STR_HEX, "hdchainid", /*optional=*/true, "The ID of the HD chain."},
     459        4850 :                         {RPCResult::Type::STR, "hdkeypath", /*optional=*/true, "The HD keypath, if the key is HD and available."},
     460        4850 :                         {RPCResult::Type::STR_HEX, "hdseedid", /*optional=*/true, "The Hash160 of the HD seed."},
     461        4850 :                         {RPCResult::Type::STR_HEX, "hdmasterfingerprint", /*optional=*/true, "The fingerprint of the master key."},
     462        9700 :                         {RPCResult::Type::ARR, "labels", "An array of labels associated with the address. Currently limited to one label but returned as an array to keep the API stable if multiple labels are enabled in the future.",
     463        9700 :                         {
     464        4850 :                             {RPCResult::Type::STR, "label name", "Label name (defaults to \"\")."},
     465             :                         }},
     466             :                     }
     467             :                 },
     468        4850 :                 RPCExamples{
     469        9700 :                     HelpExampleCli("getaddressinfo", EXAMPLE_ADDRESS[0]) +
     470        4850 :                     HelpExampleRpc("getaddressinfo", EXAMPLE_ADDRESS[0])
     471             :                 },
     472        6804 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     473             : {
     474        1954 :     const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
     475        1954 :     if (!pwallet) return UniValue::VNULL;
     476             : 
     477        1954 :     LOCK(pwallet->cs_wallet);
     478             : 
     479        1954 :     std::string error_msg;
     480        1954 :     CTxDestination dest = DecodeDestination(request.params[0].get_str(), error_msg);
     481             : 
     482             :     // Make sure the destination is valid
     483        1954 :     if (!IsValidDestination(dest)) {
     484             :         // Set generic error message in case 'DecodeDestination' didn't set it
     485          10 :         if (error_msg.empty()) error_msg = "Invalid address";
     486             : 
     487          10 :         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error_msg);
     488             :     }
     489             : 
     490        1944 :     UniValue ret(UniValue::VOBJ);
     491             : 
     492        1944 :     std::string currentAddress = EncodeDestination(dest);
     493        1944 :     ret.pushKV("address", currentAddress);
     494             : 
     495        1944 :     CScript scriptPubKey = GetScriptForDestination(dest);
     496        1944 :     ret.pushKV("scriptPubKey", HexStr(scriptPubKey));
     497             : 
     498        1944 :     std::unique_ptr<SigningProvider> provider = pwallet->GetSolvingProvider(scriptPubKey);
     499             : 
     500        1944 :     isminetype mine = pwallet->IsMine(dest);
     501        1944 :     ret.pushKV("ismine", bool(mine & ISMINE_SPENDABLE));
     502             : 
     503        3635 :     bool solvable = provider && IsSolvable(*provider, scriptPubKey);
     504        1944 :     ret.pushKV("solvable", solvable);
     505             : 
     506        1944 :     if (solvable) {
     507        1658 :         ret.pushKV("desc", InferDescriptor(scriptPubKey, *provider)->ToString());
     508        1658 :     }
     509             : 
     510        1944 :     const auto& spk_mans = pwallet->GetScriptPubKeyMans(scriptPubKey);
     511             :     // In most cases there is only one matching ScriptPubKey manager and we can't resolve ambiguity in a better way
     512        1944 :     ScriptPubKeyMan* spk_man{nullptr};
     513        1944 :     if (spk_mans.size()) spk_man = *spk_mans.begin();
     514             : 
     515        1944 :     DescriptorScriptPubKeyMan* desc_spk_man = dynamic_cast<DescriptorScriptPubKeyMan*>(spk_man);
     516        1944 :     if (desc_spk_man) {
     517         614 :         std::string desc_str;
     518         614 :         if (desc_spk_man->GetDescriptorString(desc_str, /*priv=*/false)) {
     519         614 :             ret.pushKV("parent_desc", desc_str);
     520         614 :         }
     521         614 :     }
     522             : 
     523        1944 :     ret.pushKV("iswatchonly", bool(mine & ISMINE_WATCH_ONLY));
     524             : 
     525        1944 :     UniValue detail = DescribeWalletAddress(*pwallet, dest);
     526        1944 :     ret.pushKVs(detail);
     527             : 
     528        1944 :     ret.pushKV("ischange", ScriptIsChange(*pwallet, scriptPubKey));
     529             : 
     530        1944 :     if (spk_man) {
     531        3306 :         if (const std::unique_ptr<CKeyMetadata> meta = spk_man->GetMetadata(dest)) {
     532        1615 :             ret.pushKV("timestamp", meta->nCreateTime);
     533        1615 :             CHDChain hdChainCurrent;
     534        1615 :             LegacyScriptPubKeyMan* legacy_spk_man = pwallet->GetLegacyScriptPubKeyMan();
     535        1615 :             if (legacy_spk_man != nullptr) {
     536        1028 :                 const PKHash *pkhash = std::get_if<PKHash>(&dest);
     537             :                 // TODO: refactor to use hd_seed_id from `meta`
     538        2042 :                 if (pkhash && legacy_spk_man->HaveHDKey(ToKeyID(*pkhash), hdChainCurrent)) {
     539         888 :                     ret.pushKV("hdchainid", hdChainCurrent.GetID().GetHex());
     540         888 :                 }
     541        1028 :             }
     542        1615 :             if (meta->has_key_origin) {
     543             :                 // In legacy wallets hdkeypath has always used an apostrophe for
     544             :                 // hardened derivation. Perhaps some external tool depends on that.
     545        1487 :                 ret.pushKV("hdkeypath", WriteHDKeypath(meta->key_origin.path, /*apostrophe=*/!desc_spk_man));
     546        1487 :                 ret.pushKV("hdmasterfingerprint", HexStr(meta->key_origin.fingerprint));
     547        1487 :             }
     548        1615 :         }
     549        1691 :     }
     550             : 
     551             :     // Return a `labels` array containing the label associated with the address,
     552             :     // equivalent to the `label` field above. Currently only one label can be
     553             :     // associated with an address, but we return an array so the API remains
     554             :     // stable if we allow multiple labels to be associated with an address in
     555             :     // the future.
     556        1944 :     UniValue labels(UniValue::VARR);
     557        1944 :     const auto* address_book_entry = pwallet->FindAddressBookEntry(dest);
     558        1944 :     if (address_book_entry) {
     559        1369 :         labels.push_back(address_book_entry->GetLabel());
     560        1369 :     }
     561        1944 :     ret.pushKV("labels", std::move(labels));
     562             : 
     563        1944 :     return ret;
     564        1954 : },
     565             :     };
     566           0 : }
     567             : 
     568        3133 : RPCHelpMan getaddressesbylabel()
     569             : {
     570        6266 :     return RPCHelpMan{"getaddressesbylabel",
     571        3133 :                 "\nReturns the list of addresses assigned the specified label.\n",
     572        6266 :                 {
     573        3133 :                     {"label", RPCArg::Type::STR, RPCArg::Optional::NO, "The label."},
     574             :                 },
     575        3133 :                 RPCResult{
     576        3133 :                     RPCResult::Type::OBJ_DYN, "", "json object with addresses as keys",
     577        6266 :                     {
     578        6266 :                         {RPCResult::Type::OBJ, "address", "json object with information about address",
     579        6266 :                         {
     580        3133 :                             {RPCResult::Type::STR, "purpose", "Purpose of address (\"send\" for sending address, \"receive\" for receiving address)"},
     581             :                         }},
     582             :                     }
     583             :                 },
     584        3133 :                 RPCExamples{
     585        3133 :                     HelpExampleCli("getaddressesbylabel", "\"tabby\"")
     586        3133 :             + HelpExampleRpc("getaddressesbylabel", "\"tabby\"")
     587             :                 },
     588        3370 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     589             : {
     590         237 :     const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
     591         237 :     if (!pwallet) return UniValue::VNULL;
     592             : 
     593         237 :     LOCK(pwallet->cs_wallet);
     594             : 
     595         237 :     std::string label = LabelFromValue(request.params[0]);
     596             : 
     597             :     // Find all addresses that have the given label
     598         237 :     UniValue ret(UniValue::VOBJ);
     599         237 :     std::set<std::string> addresses;
     600        4461 :     pwallet->ForEachAddrBookEntry([&](const CTxDestination& _dest, const std::string& _label, const std::string& _purpose, bool _is_change) {
     601        4224 :         if (_is_change) return;
     602        4224 :         if (_label == label) {
     603         432 :             std::string address = EncodeDestination(_dest);
     604             :             // CWallet::m_address_book is not expected to contain duplicate
     605             :             // address strings, but build a separate set as a precaution just in
     606             :             // case it does.
     607         432 :             bool unique = addresses.emplace(address).second;
     608         432 :             CHECK_NONFATAL(unique);
     609             :             // UniValue::pushKV checks if the key exists in O(N)
     610             :             // and since duplicate addresses are unexpected (checked with
     611             :             // std::set in O(log(N))), UniValue::__pushKV is used instead,
     612             :             // which currently is O(1).
     613         432 :             UniValue value(UniValue::VOBJ);
     614         432 :             value.pushKV("purpose", _purpose);
     615         432 :             ret.__pushKV(address, value);
     616         432 :         }
     617        4224 :     });
     618             : 
     619         237 :     if (ret.empty()) {
     620          30 :         throw JSONRPCError(RPC_WALLET_INVALID_LABEL_NAME, std::string("No addresses with label " + label));
     621             :     }
     622             : 
     623         207 :     return ret;
     624         267 : },
     625             :     };
     626           0 : }
     627             : 
     628        3129 : RPCHelpMan listlabels()
     629             : {
     630        6258 :     return RPCHelpMan{"listlabels",
     631        3129 :                 "\nReturns the list of all labels, or labels that are assigned to addresses with a specific purpose.\n",
     632        6258 :                 {
     633        3129 :                     {"purpose", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "Address purpose to list labels for ('send','receive'). An empty string is the same as not providing this argument."},
     634             :                 },
     635        3129 :                 RPCResult{
     636        3129 :                     RPCResult::Type::ARR, "", "",
     637        6258 :                     {
     638        3129 :                         {RPCResult::Type::STR, "label", "Label name"},
     639             :                     }
     640             :                 },
     641        3129 :                 RPCExamples{
     642             :             "\nList all labels\n"
     643        3129 :             + HelpExampleCli("listlabels", "") +
     644             :             "\nList labels that have receiving addresses\n"
     645        3129 :             + HelpExampleCli("listlabels", "receive") +
     646             :             "\nList labels that have sending addresses\n"
     647        3129 :             + HelpExampleCli("listlabels", "send") +
     648             :             "\nAs a JSON-RPC call\n"
     649        3129 :             + HelpExampleRpc("listlabels", "receive")
     650             :                 },
     651        3362 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     652             : {
     653         233 :     const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
     654         233 :     if (!pwallet) return UniValue::VNULL;
     655             : 
     656         233 :     LOCK(pwallet->cs_wallet);
     657             : 
     658         233 :     std::string purpose;
     659         233 :     if (!request.params[0].isNull()) {
     660          12 :         purpose = request.params[0].get_str();
     661          12 :     }
     662             : 
     663             :     // Add to a set to sort by label name, then insert into Univalue array
     664         233 :     std::set<std::string> label_set = pwallet->ListAddrBookLabels(purpose);
     665             : 
     666         233 :     UniValue ret(UniValue::VARR);
     667        1697 :     for (const std::string& name : label_set) {
     668        1464 :         ret.push_back(name);
     669             :     }
     670             : 
     671         233 :     return ret;
     672         233 : },
     673             :     };
     674           0 : }
     675             : 
     676             : #ifdef ENABLE_EXTERNAL_SIGNER
     677        2900 : RPCHelpMan walletdisplayaddress()
     678             : {
     679        5800 :     return RPCHelpMan{"walletdisplayaddress",
     680        2900 :         "Display address on an external signer for verification.",
     681        5800 :         {
     682        2900 :             {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "dash address to display"},
     683             :         },
     684        2900 :         RPCResult{
     685        2900 :             RPCResult::Type::OBJ,"","",
     686        5800 :             {
     687        2900 :                 {RPCResult::Type::STR, "address", "The address as confirmed by the signer"},
     688             :             }
     689             :         },
     690        2900 :         RPCExamples{""},
     691        2904 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     692             :         {
     693           4 :             std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     694           4 :             if (!wallet) return NullUniValue;
     695           4 :             CWallet* const pwallet = wallet.get();
     696             : 
     697           4 :             LOCK(pwallet->cs_wallet);
     698             : 
     699           4 :             CTxDestination dest = DecodeDestination(request.params[0].get_str());
     700             : 
     701             :             // Make sure the destination is valid
     702           4 :             if (!IsValidDestination(dest)) {
     703           0 :                 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
     704             :             }
     705             : 
     706           4 :             if (!pwallet->DisplayAddress(dest)) {
     707           0 :                 throw JSONRPCError(RPC_MISC_ERROR, "Failed to display address");
     708             :             }
     709             : 
     710           2 :             UniValue result(UniValue::VOBJ);
     711           2 :             result.pushKV("address", request.params[0].get_str());
     712           2 :             return result;
     713           4 :         }
     714             :     };
     715           0 : }
     716             : #endif // ENABLE_EXTERNAL_SIGNER
     717             : 
     718             : } // namespace wallet

Generated by: LCOV version 1.16