addresses.cpp raw

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