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
|