/ src / wallet / rpc / addresses.cpp
addresses.cpp
  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  RPCHelpMan getnewaddress()
 22  {
 23      return RPCHelpMan{
 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 RPCHelpMan& 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  RPCHelpMan getrawchangeaddress()
 73  {
 74      return RPCHelpMan{
 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 RPCHelpMan& 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  RPCHelpMan setlabel()
119  {
120      return RPCHelpMan{
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 RPCHelpMan& 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  RPCHelpMan listaddressgroupings()
158  {
159      return RPCHelpMan{
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 RPCHelpMan& 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  RPCHelpMan keypoolrefill()
219  {
220      return RPCHelpMan{"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 RPCHelpMan& 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  RPCHelpMan getaddressinfo()
369  {
370      return RPCHelpMan{
371          "getaddressinfo",
372          "Return information about the given bitcoin address.\n"
373                  "Some of the information will only be present if the address is in the active wallet.\n",
374                  {
375                      {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address for which to get information."},
376                  },
377                  RPCResult{
378                      RPCResult::Type::OBJ, "", "",
379                      {
380                          {RPCResult::Type::STR, "address", "The bitcoin address validated."},
381                          {RPCResult::Type::STR_HEX, "scriptPubKey", "The hex-encoded output script generated by the address."},
382                          {RPCResult::Type::BOOL, "ismine", "If the address is yours."},
383                          {RPCResult::Type::BOOL, "iswatchonly", "(DEPRECATED) Always false."},
384                          {RPCResult::Type::BOOL, "solvable", "If we know how to spend coins sent to this address, ignoring the possible lack of private keys."},
385                          {RPCResult::Type::STR, "desc", /*optional=*/true, "A descriptor for spending coins sent to this address (only when solvable)."},
386                          {RPCResult::Type::STR, "parent_desc", /*optional=*/true, "The descriptor used to derive this address if this is a descriptor wallet"},
387                          {RPCResult::Type::BOOL, "isscript", /*optional=*/true, "If the key is a script."},
388                          {RPCResult::Type::BOOL, "ischange", "If the address was used for change output."},
389                          {RPCResult::Type::BOOL, "iswitness", "If the address is a witness address."},
390                          {RPCResult::Type::NUM, "witness_version", /*optional=*/true, "The version number of the witness program."},
391                          {RPCResult::Type::STR_HEX, "witness_program", /*optional=*/true, "The hex value of the witness program."},
392                          {RPCResult::Type::STR, "script", /*optional=*/true, "The output script type. Only if isscript is true and the redeemscript is known. Possible\n"
393                                                                       "types: nonstandard, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_keyhash,\n"
394                              "witness_v0_scripthash, witness_unknown."},
395                          {RPCResult::Type::STR_HEX, "hex", /*optional=*/true, "The redeemscript for the p2sh address."},
396                          {RPCResult::Type::ARR, "pubkeys", /*optional=*/true, "Array of pubkeys associated with the known redeemscript (only if script is multisig).",
397                          {
398                              {RPCResult::Type::STR, "pubkey", ""},
399                          }},
400                          {RPCResult::Type::NUM, "sigsrequired", /*optional=*/true, "The number of signatures required to spend multisig output (only if script is multisig)."},
401                          {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)."},
402                          {RPCResult::Type::OBJ, "embedded", /*optional=*/true, "Information about the address embedded in P2SH or P2WSH, if relevant and known.",
403                          {
404                              {RPCResult::Type::ELISION, "", "Includes all getaddressinfo output fields for the embedded address, excluding metadata (timestamp, hdkeypath, hdseedid)\n"
405                              "and relation to the wallet (ismine)."},
406                          }},
407                          {RPCResult::Type::BOOL, "iscompressed", /*optional=*/true, "If the pubkey is compressed."},
408                          {RPCResult::Type::NUM_TIME, "timestamp", /*optional=*/true, "The creation time of the key, if available, expressed in " + UNIX_EPOCH_TIME + "."},
409                          {RPCResult::Type::STR, "hdkeypath", /*optional=*/true, "The HD keypath, if the key is HD and available."},
410                          {RPCResult::Type::STR_HEX, "hdseedid", /*optional=*/true, "The Hash160 of the HD seed."},
411                          {RPCResult::Type::STR_HEX, "hdmasterfingerprint", /*optional=*/true, "The fingerprint of the master key."},
412                          {RPCResult::Type::ARR, "labels", "Array of labels associated with the address. Currently limited to one label but returned\n"
413                              "as an array to keep the API stable if multiple labels are enabled in the future.",
414                          {
415                              {RPCResult::Type::STR, "label name", "Label name (defaults to \"\")."},
416                          }},
417                      }
418                  },
419                  RPCExamples{
420                      HelpExampleCli("getaddressinfo", "\"" + EXAMPLE_ADDRESS[0] + "\"") +
421                      HelpExampleRpc("getaddressinfo", "\"" + EXAMPLE_ADDRESS[0] + "\"")
422                  },
423          [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
424  {
425      const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
426      if (!pwallet) return UniValue::VNULL;
427  
428      LOCK(pwallet->cs_wallet);
429  
430      std::string error_msg;
431      CTxDestination dest = DecodeDestination(request.params[0].get_str(), error_msg);
432  
433      // Make sure the destination is valid
434      if (!IsValidDestination(dest)) {
435          // Set generic error message in case 'DecodeDestination' didn't set it
436          if (error_msg.empty()) error_msg = "Invalid address";
437  
438          throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error_msg);
439      }
440  
441      UniValue ret(UniValue::VOBJ);
442  
443      std::string currentAddress = EncodeDestination(dest);
444      ret.pushKV("address", currentAddress);
445  
446      CScript scriptPubKey = GetScriptForDestination(dest);
447      ret.pushKV("scriptPubKey", HexStr(scriptPubKey));
448  
449      std::unique_ptr<SigningProvider> provider = pwallet->GetSolvingProvider(scriptPubKey);
450  
451      bool mine = pwallet->IsMine(dest);
452      ret.pushKV("ismine", mine);
453  
454      if (provider) {
455          auto inferred = InferDescriptor(scriptPubKey, *provider);
456          bool solvable = inferred->IsSolvable();
457          ret.pushKV("solvable", solvable);
458          if (solvable) {
459              ret.pushKV("desc", inferred->ToString());
460          }
461      } else {
462          ret.pushKV("solvable", false);
463      }
464  
465      const auto& spk_mans = pwallet->GetScriptPubKeyMans(scriptPubKey);
466      // In most cases there is only one matching ScriptPubKey manager and we can't resolve ambiguity in a better way
467      ScriptPubKeyMan* spk_man{nullptr};
468      if (spk_mans.size()) spk_man = *spk_mans.begin();
469  
470      DescriptorScriptPubKeyMan* desc_spk_man = dynamic_cast<DescriptorScriptPubKeyMan*>(spk_man);
471      if (desc_spk_man) {
472          std::string desc_str;
473          if (desc_spk_man->GetDescriptorString(desc_str, /*priv=*/false)) {
474              ret.pushKV("parent_desc", desc_str);
475          }
476      }
477  
478      ret.pushKV("iswatchonly", false);
479  
480      UniValue detail = DescribeWalletAddress(*pwallet, dest);
481      ret.pushKVs(std::move(detail));
482  
483      ret.pushKV("ischange", ScriptIsChange(*pwallet, scriptPubKey));
484  
485      if (spk_man) {
486          if (const std::unique_ptr<CKeyMetadata> meta = spk_man->GetMetadata(dest)) {
487              ret.pushKV("timestamp", meta->nCreateTime);
488              if (meta->has_key_origin) {
489                  // In legacy wallets hdkeypath has always used an apostrophe for
490                  // hardened derivation. Perhaps some external tool depends on that.
491                  ret.pushKV("hdkeypath", WriteHDKeypath(meta->key_origin.path, /*apostrophe=*/!desc_spk_man));
492                  ret.pushKV("hdseedid", meta->hd_seed_id.GetHex());
493                  ret.pushKV("hdmasterfingerprint", HexStr(meta->key_origin.fingerprint));
494              }
495          }
496      }
497  
498      // Return a `labels` array containing the label associated with the address,
499      // equivalent to the `label` field above. Currently only one label can be
500      // associated with an address, but we return an array so the API remains
501      // stable if we allow multiple labels to be associated with an address in
502      // the future.
503      UniValue labels(UniValue::VARR);
504      const auto* address_book_entry = pwallet->FindAddressBookEntry(dest);
505      if (address_book_entry) {
506          labels.push_back(address_book_entry->GetLabel());
507      }
508      ret.pushKV("labels", std::move(labels));
509  
510      return ret;
511  },
512      };
513  }
514  
515  RPCHelpMan getaddressesbylabel()
516  {
517      return RPCHelpMan{
518          "getaddressesbylabel",
519          "Returns the list of addresses assigned the specified label.\n",
520                  {
521                      {"label", RPCArg::Type::STR, RPCArg::Optional::NO, "The label."},
522                  },
523                  RPCResult{
524                      RPCResult::Type::OBJ_DYN, "", "json object with addresses as keys",
525                      {
526                          {RPCResult::Type::OBJ, "address", "json object with information about address",
527                          {
528                              {RPCResult::Type::STR, "purpose", "Purpose of address (\"send\" for sending address, \"receive\" for receiving address)"},
529                          }},
530                      }
531                  },
532                  RPCExamples{
533                      HelpExampleCli("getaddressesbylabel", "\"tabby\"")
534              + HelpExampleRpc("getaddressesbylabel", "\"tabby\"")
535                  },
536          [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
537  {
538      const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
539      if (!pwallet) return UniValue::VNULL;
540  
541      LOCK(pwallet->cs_wallet);
542  
543      const std::string label{LabelFromValue(request.params[0])};
544  
545      // Find all addresses that have the given label
546      UniValue ret(UniValue::VOBJ);
547      std::set<std::string> addresses;
548      pwallet->ForEachAddrBookEntry([&](const CTxDestination& _dest, const std::string& _label, bool _is_change, const std::optional<AddressPurpose>& _purpose) {
549          if (_is_change) return;
550          if (_label == label) {
551              std::string address = EncodeDestination(_dest);
552              // CWallet::m_address_book is not expected to contain duplicate
553              // address strings, but build a separate set as a precaution just in
554              // case it does.
555              bool unique = addresses.emplace(address).second;
556              CHECK_NONFATAL(unique);
557              // UniValue::pushKV checks if the key exists in O(N)
558              // and since duplicate addresses are unexpected (checked with
559              // std::set in O(log(N))), UniValue::pushKVEnd is used instead,
560              // which currently is O(1).
561              UniValue value(UniValue::VOBJ);
562              value.pushKV("purpose", _purpose ? PurposeToString(*_purpose) : "unknown");
563              ret.pushKVEnd(address, std::move(value));
564          }
565      });
566  
567      if (ret.empty()) {
568          throw JSONRPCError(RPC_WALLET_INVALID_LABEL_NAME, std::string("No addresses with label " + label));
569      }
570  
571      return ret;
572  },
573      };
574  }
575  
576  RPCHelpMan listlabels()
577  {
578      return RPCHelpMan{
579          "listlabels",
580          "Returns the list of all labels, or labels that are assigned to addresses with a specific purpose.\n",
581                  {
582                      {"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."},
583                  },
584                  RPCResult{
585                      RPCResult::Type::ARR, "", "",
586                      {
587                          {RPCResult::Type::STR, "label", "Label name"},
588                      }
589                  },
590                  RPCExamples{
591              "\nList all labels\n"
592              + HelpExampleCli("listlabels", "") +
593              "\nList labels that have receiving addresses\n"
594              + HelpExampleCli("listlabels", "receive") +
595              "\nList labels that have sending addresses\n"
596              + HelpExampleCli("listlabels", "send") +
597              "\nAs a JSON-RPC call\n"
598              + HelpExampleRpc("listlabels", "receive")
599                  },
600          [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
601  {
602      const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
603      if (!pwallet) return UniValue::VNULL;
604  
605      LOCK(pwallet->cs_wallet);
606  
607      std::optional<AddressPurpose> purpose;
608      if (!request.params[0].isNull()) {
609          std::string purpose_str = request.params[0].get_str();
610          if (!purpose_str.empty()) {
611              purpose = PurposeFromString(purpose_str);
612              if (!purpose) {
613                  throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid 'purpose' argument, must be a known purpose string, typically 'send', or 'receive'.");
614              }
615          }
616      }
617  
618      // Add to a set to sort by label name, then insert into Univalue array
619      std::set<std::string> label_set = pwallet->ListAddrBookLabels(purpose);
620  
621      UniValue ret(UniValue::VARR);
622      for (const std::string& name : label_set) {
623          ret.push_back(name);
624      }
625  
626      return ret;
627  },
628      };
629  }
630  
631  
632  #ifdef ENABLE_EXTERNAL_SIGNER
633  RPCHelpMan walletdisplayaddress()
634  {
635      return RPCHelpMan{
636          "walletdisplayaddress",
637          "Display address on an external signer for verification.",
638          {
639              {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "bitcoin address to display"},
640          },
641          RPCResult{
642              RPCResult::Type::OBJ,"","",
643              {
644                  {RPCResult::Type::STR, "address", "The address as confirmed by the signer"},
645              }
646          },
647          RPCExamples{""},
648          [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
649          {
650              std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
651              if (!wallet) return UniValue::VNULL;
652              CWallet* const pwallet = wallet.get();
653  
654              LOCK(pwallet->cs_wallet);
655  
656              CTxDestination dest = DecodeDestination(request.params[0].get_str());
657  
658              // Make sure the destination is valid
659              if (!IsValidDestination(dest)) {
660                  throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
661              }
662  
663              util::Result<void> res = pwallet->DisplayAddress(dest);
664              if (!res) throw JSONRPCError(RPC_MISC_ERROR, util::ErrorString(res).original);
665  
666              UniValue result(UniValue::VOBJ);
667              result.pushKV("address", request.params[0].get_str());
668              return result;
669          }
670      };
671  }
672  #endif // ENABLE_EXTERNAL_SIGNER
673  } // namespace wallet